[Pkg-javascript-commits] [node-serve-index] 01/02: Imported Upstream version 1.4.0
Leo Iannacone
l3on-guest at moszumanska.debian.org
Tue Oct 14 11:40:10 UTC 2014
This is an automated email from the git hooks/post-receive script.
l3on-guest pushed a commit to branch master
in repository node-serve-index.
commit ae7258e9d0ad9b9543bf4a78284499541c13e54b
Author: Leo Iannacone <l3on at ubuntu.com>
Date: Tue Oct 14 12:11:24 2014 +0200
Imported Upstream version 1.4.0
---
.travis.yml | 11 +
HISTORY.md | 95 ++++
LICENSE | 25 +
README.md | 127 +++++
index.js | 592 ++++++++++++++++++++
package.json | 35 ++
public/directory.html | 82 +++
public/icons/application_xp.png | Bin 0 -> 426 bytes
public/icons/application_xp_terminal.png | Bin 0 -> 507 bytes
public/icons/box.png | Bin 0 -> 555 bytes
public/icons/cd.png | Bin 0 -> 673 bytes
public/icons/controller.png | Bin 0 -> 666 bytes
public/icons/drive.png | Bin 0 -> 346 bytes
public/icons/film.png | Bin 0 -> 653 bytes
public/icons/folder.png | Bin 0 -> 634 bytes
public/icons/font.png | Bin 0 -> 567 bytes
public/icons/image.png | Bin 0 -> 516 bytes
public/icons/map.png | Bin 0 -> 804 bytes
public/icons/page.png | Bin 0 -> 635 bytes
public/icons/page_add.png | Bin 0 -> 739 bytes
public/icons/page_attach.png | Bin 0 -> 794 bytes
public/icons/page_code.png | Bin 0 -> 818 bytes
public/icons/page_copy.png | Bin 0 -> 663 bytes
public/icons/page_delete.png | Bin 0 -> 740 bytes
public/icons/page_edit.png | Bin 0 -> 807 bytes
public/icons/page_error.png | Bin 0 -> 793 bytes
public/icons/page_excel.png | Bin 0 -> 817 bytes
public/icons/page_find.png | Bin 0 -> 879 bytes
public/icons/page_gear.png | Bin 0 -> 833 bytes
public/icons/page_go.png | Bin 0 -> 779 bytes
public/icons/page_green.png | Bin 0 -> 621 bytes
public/icons/page_key.png | Bin 0 -> 801 bytes
public/icons/page_lightning.png | Bin 0 -> 839 bytes
public/icons/page_link.png | Bin 0 -> 830 bytes
public/icons/page_paintbrush.png | Bin 0 -> 813 bytes
public/icons/page_paste.png | Bin 0 -> 703 bytes
public/icons/page_red.png | Bin 0 -> 641 bytes
public/icons/page_refresh.png | Bin 0 -> 858 bytes
public/icons/page_save.png | Bin 0 -> 774 bytes
public/icons/page_white.png | Bin 0 -> 294 bytes
public/icons/page_white_acrobat.png | Bin 0 -> 591 bytes
public/icons/page_white_actionscript.png | Bin 0 -> 664 bytes
public/icons/page_white_add.png | Bin 0 -> 512 bytes
public/icons/page_white_c.png | Bin 0 -> 587 bytes
public/icons/page_white_camera.png | Bin 0 -> 656 bytes
public/icons/page_white_cd.png | Bin 0 -> 666 bytes
public/icons/page_white_code.png | Bin 0 -> 603 bytes
public/icons/page_white_code_red.png | Bin 0 -> 587 bytes
public/icons/page_white_coldfusion.png | Bin 0 -> 592 bytes
public/icons/page_white_compressed.png | Bin 0 -> 724 bytes
public/icons/page_white_copy.png | Bin 0 -> 309 bytes
public/icons/page_white_cplusplus.png | Bin 0 -> 621 bytes
public/icons/page_white_csharp.png | Bin 0 -> 700 bytes
public/icons/page_white_cup.png | Bin 0 -> 639 bytes
public/icons/page_white_database.png | Bin 0 -> 579 bytes
public/icons/page_white_delete.png | Bin 0 -> 536 bytes
public/icons/page_white_dvd.png | Bin 0 -> 638 bytes
public/icons/page_white_edit.png | Bin 0 -> 618 bytes
public/icons/page_white_error.png | Bin 0 -> 623 bytes
public/icons/page_white_excel.png | Bin 0 -> 663 bytes
public/icons/page_white_find.png | Bin 0 -> 676 bytes
public/icons/page_white_flash.png | Bin 0 -> 582 bytes
public/icons/page_white_freehand.png | Bin 0 -> 639 bytes
public/icons/page_white_gear.png | Bin 0 -> 402 bytes
public/icons/page_white_get.png | Bin 0 -> 516 bytes
public/icons/page_white_go.png | Bin 0 -> 612 bytes
public/icons/page_white_h.png | Bin 0 -> 603 bytes
public/icons/page_white_horizontal.png | Bin 0 -> 296 bytes
public/icons/page_white_key.png | Bin 0 -> 616 bytes
public/icons/page_white_lightning.png | Bin 0 -> 669 bytes
public/icons/page_white_link.png | Bin 0 -> 614 bytes
public/icons/page_white_magnify.png | Bin 0 -> 554 bytes
public/icons/page_white_medal.png | Bin 0 -> 706 bytes
public/icons/page_white_office.png | Bin 0 -> 779 bytes
public/icons/page_white_paint.png | Bin 0 -> 688 bytes
public/icons/page_white_paintbrush.png | Bin 0 -> 618 bytes
public/icons/page_white_paste.png | Bin 0 -> 620 bytes
public/icons/page_white_php.png | Bin 0 -> 538 bytes
public/icons/page_white_picture.png | Bin 0 -> 650 bytes
public/icons/page_white_powerpoint.png | Bin 0 -> 588 bytes
public/icons/page_white_put.png | Bin 0 -> 523 bytes
public/icons/page_white_ruby.png | Bin 0 -> 626 bytes
public/icons/page_white_stack.png | Bin 0 -> 317 bytes
public/icons/page_white_star.png | Bin 0 -> 565 bytes
public/icons/page_white_swoosh.png | Bin 0 -> 634 bytes
public/icons/page_white_text.png | Bin 0 -> 342 bytes
public/icons/page_white_text_width.png | Bin 0 -> 315 bytes
public/icons/page_white_tux.png | Bin 0 -> 668 bytes
public/icons/page_white_vector.png | Bin 0 -> 644 bytes
public/icons/page_white_visualstudio.png | Bin 0 -> 702 bytes
public/icons/page_white_width.png | Bin 0 -> 309 bytes
public/icons/page_white_word.png | Bin 0 -> 651 bytes
public/icons/page_white_world.png | Bin 0 -> 734 bytes
public/icons/page_white_wrench.png | Bin 0 -> 613 bytes
public/icons/page_white_zip.png | Bin 0 -> 386 bytes
public/icons/page_word.png | Bin 0 -> 777 bytes
public/icons/page_world.png | Bin 0 -> 903 bytes
public/style.css | 257 +++++++++
test/fixtures/#directory/index.html | 1 +
test/fixtures/.hidden | 1 +
test/fixtures/collect/sample | 0
test/fixtures/collect/sample.jpg | 0
test/fixtures/collect/sample.mp4 | 0
test/fixtures/collect/sample.pdf | 0
test/fixtures/collect/sample.qfx | 0
test/fixtures/collect/sample.rdf | 0
test/fixtures/collect/sample.txt | 0
test/fixtures/collect/sample.xlsx | 0
test/fixtures/file #1.txt | 1 +
test/fixtures/foo bar | 1 +
test/fixtures/g# %3 o %2525 %37 dir/empty.txt | 0
test/fixtures/nums | 1 +
test/fixtures/todo.txt | 1 +
test/fixtures/users/index.html | 1 +
test/fixtures/users/tobi.txt | 1 +
.../\343\201\225\343\201\217\343\202\211.txt" | 0
test/shared/index.js | 26 +
test/shared/styles.css | 3 +
test/shared/template.html | 15 +
test/test.js | 618 +++++++++++++++++++++
120 files changed, 1894 insertions(+)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1ff243c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: node_js
+node_js:
+ - "0.8"
+ - "0.10"
+ - "0.11"
+matrix:
+ allow_failures:
+ - node_js: "0.11"
+ fast_finish: true
+script: "npm run-script test-travis"
+after_script: "npm install coveralls at 2.10.0 && cat ./coverage/lcov.info | coveralls"
diff --git a/HISTORY.md b/HISTORY.md
new file mode 100644
index 0000000..2801483
--- /dev/null
+++ b/HISTORY.md
@@ -0,0 +1,95 @@
+1.4.0 / 2014-10-03
+==================
+
+ * Add `dir` argument to `filter` function
+ * Support using tokens multiple times
+
+1.3.1 / 2014-10-01
+==================
+
+ * Fix incorrect 403 on Windows and Node.js 0.11
+ * deps: accepts@~1.1.1
+ - deps: mime-types@~2.0.2
+ - deps: negotiator at 0.4.8
+
+1.3.0 / 2014-09-20
+==================
+
+ * Add icon for mkv files
+ * Lookup icon by mime type for greater icon support
+
+1.2.1 / 2014-09-05
+==================
+
+ * deps: accepts@~1.1.0
+ * deps: debug@~2.0.0
+
+1.2.0 / 2014-08-25
+==================
+
+ * Add `debug` messages
+ * Resolve relative paths at middleware setup
+
+1.1.6 / 2014-08-10
+==================
+
+ * Fix URL parsing
+ * deps: parseurl@~1.3.0
+
+1.1.5 / 2014-07-27
+==================
+
+ * Fix Content-Length calculation for multi-byte file names
+ * deps: accepts@~1.0.7
+ - deps: negotiator at 0.4.7
+
+1.1.4 / 2014-06-20
+==================
+
+ * deps: accepts@~1.0.5
+
+1.1.3 / 2014-06-20
+==================
+
+ * deps: accepts@~1.0.4
+ - use `mime-types`
+
+1.1.2 / 2014-06-19
+==================
+
+ * deps: batch at 0.5.1
+
+1.1.1 / 2014-06-11
+==================
+
+ * deps: accepts at 1.0.3
+
+1.1.0 / 2014-05-29
+==================
+
+ * Fix content negotiation when no `Accept` header
+ * Properly support all HTTP methods
+ * Support vanilla node.js http servers
+ * Treat `ENAMETOOLONG` as code 414
+ * Use accepts for negotiation
+
+1.0.3 / 2014-05-20
+==================
+
+ * Fix error from non-statable files in HTML view
+
+1.0.2 / 2014-04-28
+==================
+
+ * Add `stylesheet` option
+ * deps: negotiator at 0.4.3
+
+1.0.1 / 2014-03-05
+==================
+
+ * deps: negotiator at 0.4.2
+
+1.0.0 / 2014-03-05
+==================
+
+ * Genesis from connect
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b7bc085
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8874086
--- /dev/null
+++ b/README.md
@@ -0,0 +1,127 @@
+# serve-index
+
+[![NPM Version][npm-image]][npm-url]
+[![NPM Downloads][downloads-image]][downloads-url]
+[![Build Status][travis-image]][travis-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+[![Gittip][gittip-image]][gittip-url]
+
+ Serves pages that contain directory listings for a given path.
+
+## Install
+
+```sh
+$ npm install serve-index
+```
+
+## API
+
+```js
+var serveIndex = require('serve-index')
+```
+
+### serveIndex(path, options)
+
+Returns middlware that serves an index of the directory in the given `path`.
+
+The `path` is based off the `req.url` value, so a `req.url` of `'/some/dir`
+with a `path` of `'public'` will look at `'public/some/dir'`. If you are using
+something like `express`, you can change the URL "base" with `app.use` (see
+the express example).
+
+#### Options
+
+Serve index accepts these properties in the options object.
+
+##### filter
+
+Apply this filter function to files. Defaults to `false`. The `filter` function
+is called for each file, with the signature `filter(filename, index, files, dir)`
+where `filename` is the name of the file, `index` is the array index, `files` is
+the array of files and `dir` is the absolute path the file is located (and thus,
+the directory the listing is for).
+
+##### hidden
+
+Display hidden (dot) files. Defaults to `false`.
+
+##### icons
+
+Display icons. Defaults to `false`.
+
+##### stylesheet
+
+Optional path to a CSS stylesheet. Defaults to a built-in stylesheet.
+
+##### template
+
+Optional path to an HTML template. Defaults to a built-in template.
+
+The following tokens are replaced in templates:
+
+ * `{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 specified stylesheet and embedded images.
+
+##### view
+
+Display mode. `tiles` and `details` are available. Defaults to `tiles`.
+
+## Examples
+
+### Serve directory indexes with vanilla node.js http server
+
+```js
+var finalhandler = require('finalhandler')
+var http = require('http')
+var serveIndex = require('serve-index')
+var serveStatic = require('serve-static')
+
+// Serve directory indexes for public/ftp folder (with icons)
+var index = serveIndex('public/ftp', {'icons': true})
+
+// Serve up public/ftp folder files
+var serve = serveStatic('public/ftp')
+
+// Create server
+var server = http.createServer(function onRequest(req, res){
+ var done = finalhandler(req, res)
+ serve(req, res, function onNext(err) {
+ if (err) return done(err)
+ index(req, res, done)
+ })
+})
+
+// Listen
+server.listen(3000)
+```
+
+### Serve directory indexes with express
+
+```js
+var express = require('express')
+var serveIndex = require('serve-index')
+
+var app = express()
+
+// Serve URLs like /ftp/thing as public/ftp/thing
+app.use('/ftp', serveIndex('public/ftp', {'icons': true}))
+app.listen()
+```
+
+## License
+
+[MIT](LICENSE). The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons
+are created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).
+
+[npm-image]: https://img.shields.io/npm/v/serve-index.svg?style=flat
+[npm-url]: https://npmjs.org/package/serve-index
+[travis-image]: https://img.shields.io/travis/expressjs/serve-index.svg?style=flat
+[travis-url]: https://travis-ci.org/expressjs/serve-index
+[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index.svg?style=flat
+[coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master
+[downloads-image]: https://img.shields.io/npm/dm/serve-index.svg?style=flat
+[downloads-url]: https://npmjs.org/package/serve-index
+[gittip-image]: https://img.shields.io/gittip/dougwilson.svg?style=flat
+[gittip-url]: https://www.gittip.com/dougwilson/
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..7b62fca
--- /dev/null
+++ b/index.js
@@ -0,0 +1,592 @@
+
+/*!
+ * serve-index
+ * 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 accepts = require('accepts');
+var debug = require('debug')('serve-index');
+var http = require('http')
+ , 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');
+var parseUrl = require('parseurl');
+var resolve = require('path').resolve;
+
+/*!
+ * Icon cache.
+ */
+
+var cache = {};
+
+/*!
+ * Default template.
+ */
+
+var defaultTemplate = join(__dirname, 'public', 'directory.html');
+
+/*!
+ * Stylesheet.
+ */
+
+var defaultStylesheet = 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'
+};
+
+/**
+ * Serve directory listings with the given `root` path.
+ *
+ * See Readme.md for documentation of options.
+ *
+ * @param {String} path
+ * @param {Object} options
+ * @return {Function} middleware
+ * @api public
+ */
+
+exports = module.exports = function serveIndex(root, options){
+ options = options || {};
+
+ // root required
+ if (!root) throw new TypeError('serveIndex() root path required');
+
+ // resolve root to absolute and normalize
+ root = resolve(root);
+ root = normalize(root + sep);
+
+ var hidden = options.hidden
+ , icons = options.icons
+ , view = options.view || 'tiles'
+ , filter = options.filter
+ , template = options.template || defaultTemplate
+ , stylesheet = options.stylesheet || defaultStylesheet;
+
+ return function serveIndex(req, res, next) {
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
+ res.statusCode = 'OPTIONS' === req.method
+ ? 200
+ : 405;
+ res.setHeader('Allow', 'GET, HEAD, OPTIONS');
+ res.end();
+ return;
+ }
+
+ // parse URLs
+ var url = parseUrl(req);
+ var originalUrl = parseUrl.original(req);
+ var dir = decodeURIComponent(url.pathname);
+ var originalDir = decodeURIComponent(originalUrl.pathname);
+
+ // join / normalize from root dir
+ var path = normalize(join(root, dir));
+
+ // null byte(s), bad request
+ if (~path.indexOf('\0')) return next(createError(400));
+
+ // malicious path
+ if ((path + sep).substr(0, root.length) !== root) {
+ debug('malicious path "%s"', path);
+ return next(createError(403));
+ }
+
+ // determine ".." display
+ var showUp = normalize(resolve(path) + sep) !== root;
+
+ // check if we have a directory
+ debug('stat "%s"', path);
+ fs.stat(path, function(err, stat){
+ if (err && err.code === 'ENOENT') {
+ return next();
+ }
+
+ if (err) {
+ err.status = err.code === 'ENAMETOOLONG'
+ ? 414
+ : 500;
+ return next(err);
+ }
+
+ if (!stat.isDirectory()) return next();
+
+ // fetch files
+ debug('readdir "%s"', path);
+ fs.readdir(path, function(err, files){
+ if (err) return next(err);
+ if (!hidden) files = removeHidden(files);
+ if (filter) files = files.filter(function(filename, index, list) {
+ return filter(filename, index, list, path);
+ });
+ files.sort();
+
+ // content-negotiation
+ var accept = accepts(req);
+ var type = accept.types(mediaTypes);
+
+ // not acceptable
+ if (!type) return next(createError(406));
+ exports[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet);
+ });
+ });
+ };
+};
+
+/**
+ * Respond with text/html.
+ */
+
+exports.html = function(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet){
+ 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\}/g, style.concat(iconStyle(files, icons)))
+ .replace(/\{files\}/g, html(files, dir, icons, view))
+ .replace(/\{directory\}/g, dir)
+ .replace(/\{linked-path\}/g, htmlPath(dir));
+
+ var buf = new Buffer(str, 'utf8');
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
+ res.setHeader('Content-Length', buf.length);
+ res.end(buf);
+ });
+ });
+ });
+};
+
+/**
+ * Respond with application/json.
+ */
+
+exports.json = function(req, res, files){
+ var body = JSON.stringify(files);
+ var buf = new Buffer(body, 'utf8');
+
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
+ res.setHeader('Content-Length', buf.length);
+ res.end(buf);
+};
+
+/**
+ * Respond with text/plain.
+ */
+
+exports.plain = function(req, res, files){
+ var body = files.join('\n') + '\n';
+ var buf = new Buffer(body, 'utf8');
+
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
+ res.setHeader('Content-Length', buf.length);
+ res.end(buf);
+};
+
+/**
+ * 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(' / ');
+}
+
+/**
+ * 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 className;
+ var i;
+ var iconName;
+ 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());
+ 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;
+}
+
+/**
+ * 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) {
+ 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.toDateString() + ' ' + file.stat.mtime.toLocaleTimeString()
+ : '';
+ var size = file.stat && !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), 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
+ '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/font-woff': 'font.png',
+ '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-font-ttf': 'font.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',
+ '.otf': 'font.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'
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e2af38b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "serve-index",
+ "description": "Serve directory listings",
+ "version": "1.4.0",
+ "author": "Douglas Christopher Wilson <doug at somethingdoug.com>",
+ "license": "MIT",
+ "repository": "expressjs/serve-index",
+ "dependencies": {
+ "accepts": "~1.1.1",
+ "batch": "0.5.1",
+ "debug": "~2.0.0",
+ "mime-types": "~2.0.1",
+ "parseurl": "~1.3.0"
+ },
+ "devDependencies": {
+ "istanbul": "0.3.2",
+ "mocha": "~1.21.1",
+ "should": "~4.0.0",
+ "supertest": "~0.14.0"
+ },
+ "files": [
+ "public/",
+ "LICENSE",
+ "HISTORY.md",
+ "index.js"
+ ],
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "scripts": {
+ "test": "mocha --reporter spec --bail --check-leaks test/",
+ "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
+ "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
+ }
+}
diff --git a/public/directory.html b/public/directory.html
new file mode 100644
index 0000000..8ed8b4a
--- /dev/null
+++ b/public/directory.html
@@ -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>
\ No newline at end of file
diff --git a/public/icons/application_xp.png b/public/icons/application_xp.png
new file mode 100644
index 0000000..d22860a
Binary files /dev/null and b/public/icons/application_xp.png differ
diff --git a/public/icons/application_xp_terminal.png b/public/icons/application_xp_terminal.png
new file mode 100644
index 0000000..c28dd63
Binary files /dev/null and b/public/icons/application_xp_terminal.png differ
diff --git a/public/icons/box.png b/public/icons/box.png
new file mode 100644
index 0000000..8443c23
Binary files /dev/null and b/public/icons/box.png differ
diff --git a/public/icons/cd.png b/public/icons/cd.png
new file mode 100644
index 0000000..ef43223
Binary files /dev/null and b/public/icons/cd.png differ
diff --git a/public/icons/controller.png b/public/icons/controller.png
new file mode 100644
index 0000000..5cf76ed
Binary files /dev/null and b/public/icons/controller.png differ
diff --git a/public/icons/drive.png b/public/icons/drive.png
new file mode 100644
index 0000000..37b7c9b
Binary files /dev/null and b/public/icons/drive.png differ
diff --git a/public/icons/film.png b/public/icons/film.png
new file mode 100644
index 0000000..b0ce7bb
Binary files /dev/null and b/public/icons/film.png differ
diff --git a/public/icons/folder.png b/public/icons/folder.png
new file mode 100644
index 0000000..698f3d3
Binary files /dev/null and b/public/icons/folder.png differ
diff --git a/public/icons/font.png b/public/icons/font.png
new file mode 100644
index 0000000..b7960db
Binary files /dev/null and b/public/icons/font.png differ
diff --git a/public/icons/image.png b/public/icons/image.png
new file mode 100644
index 0000000..fc3c393
Binary files /dev/null and b/public/icons/image.png differ
diff --git a/public/icons/map.png b/public/icons/map.png
new file mode 100644
index 0000000..f90ef25
Binary files /dev/null and b/public/icons/map.png differ
diff --git a/public/icons/page.png b/public/icons/page.png
new file mode 100644
index 0000000..03ddd79
Binary files /dev/null and b/public/icons/page.png differ
diff --git a/public/icons/page_add.png b/public/icons/page_add.png
new file mode 100644
index 0000000..d5bfa07
Binary files /dev/null and b/public/icons/page_add.png differ
diff --git a/public/icons/page_attach.png b/public/icons/page_attach.png
new file mode 100644
index 0000000..89ee2da
Binary files /dev/null and b/public/icons/page_attach.png differ
diff --git a/public/icons/page_code.png b/public/icons/page_code.png
new file mode 100644
index 0000000..f7ea904
Binary files /dev/null and b/public/icons/page_code.png differ
diff --git a/public/icons/page_copy.png b/public/icons/page_copy.png
new file mode 100644
index 0000000..195dc6d
Binary files /dev/null and b/public/icons/page_copy.png differ
diff --git a/public/icons/page_delete.png b/public/icons/page_delete.png
new file mode 100644
index 0000000..3141467
Binary files /dev/null and b/public/icons/page_delete.png differ
diff --git a/public/icons/page_edit.png b/public/icons/page_edit.png
new file mode 100644
index 0000000..046811e
Binary files /dev/null and b/public/icons/page_edit.png differ
diff --git a/public/icons/page_error.png b/public/icons/page_error.png
new file mode 100644
index 0000000..f07f449
Binary files /dev/null and b/public/icons/page_error.png differ
diff --git a/public/icons/page_excel.png b/public/icons/page_excel.png
new file mode 100644
index 0000000..eb6158e
Binary files /dev/null and b/public/icons/page_excel.png differ
diff --git a/public/icons/page_find.png b/public/icons/page_find.png
new file mode 100644
index 0000000..2f19388
Binary files /dev/null and b/public/icons/page_find.png differ
diff --git a/public/icons/page_gear.png b/public/icons/page_gear.png
new file mode 100644
index 0000000..8e83281
Binary files /dev/null and b/public/icons/page_gear.png differ
diff --git a/public/icons/page_go.png b/public/icons/page_go.png
new file mode 100644
index 0000000..80fe1ed
Binary files /dev/null and b/public/icons/page_go.png differ
diff --git a/public/icons/page_green.png b/public/icons/page_green.png
new file mode 100644
index 0000000..de8e003
Binary files /dev/null and b/public/icons/page_green.png differ
diff --git a/public/icons/page_key.png b/public/icons/page_key.png
new file mode 100644
index 0000000..d6626cb
Binary files /dev/null and b/public/icons/page_key.png differ
diff --git a/public/icons/page_lightning.png b/public/icons/page_lightning.png
new file mode 100644
index 0000000..7e56870
Binary files /dev/null and b/public/icons/page_lightning.png differ
diff --git a/public/icons/page_link.png b/public/icons/page_link.png
new file mode 100644
index 0000000..312eab0
Binary files /dev/null and b/public/icons/page_link.png differ
diff --git a/public/icons/page_paintbrush.png b/public/icons/page_paintbrush.png
new file mode 100644
index 0000000..246a2f0
Binary files /dev/null and b/public/icons/page_paintbrush.png differ
diff --git a/public/icons/page_paste.png b/public/icons/page_paste.png
new file mode 100644
index 0000000..968f073
Binary files /dev/null and b/public/icons/page_paste.png differ
diff --git a/public/icons/page_red.png b/public/icons/page_red.png
new file mode 100644
index 0000000..0b18247
Binary files /dev/null and b/public/icons/page_red.png differ
diff --git a/public/icons/page_refresh.png b/public/icons/page_refresh.png
new file mode 100644
index 0000000..cf347c7
Binary files /dev/null and b/public/icons/page_refresh.png differ
diff --git a/public/icons/page_save.png b/public/icons/page_save.png
new file mode 100644
index 0000000..caea546
Binary files /dev/null and b/public/icons/page_save.png differ
diff --git a/public/icons/page_white.png b/public/icons/page_white.png
new file mode 100644
index 0000000..8b8b1ca
Binary files /dev/null and b/public/icons/page_white.png differ
diff --git a/public/icons/page_white_acrobat.png b/public/icons/page_white_acrobat.png
new file mode 100644
index 0000000..8f8095e
Binary files /dev/null and b/public/icons/page_white_acrobat.png differ
diff --git a/public/icons/page_white_actionscript.png b/public/icons/page_white_actionscript.png
new file mode 100644
index 0000000..159b240
Binary files /dev/null and b/public/icons/page_white_actionscript.png differ
diff --git a/public/icons/page_white_add.png b/public/icons/page_white_add.png
new file mode 100644
index 0000000..aa23dde
Binary files /dev/null and b/public/icons/page_white_add.png differ
diff --git a/public/icons/page_white_c.png b/public/icons/page_white_c.png
new file mode 100644
index 0000000..34a05cc
Binary files /dev/null and b/public/icons/page_white_c.png differ
diff --git a/public/icons/page_white_camera.png b/public/icons/page_white_camera.png
new file mode 100644
index 0000000..f501a59
Binary files /dev/null and b/public/icons/page_white_camera.png differ
diff --git a/public/icons/page_white_cd.png b/public/icons/page_white_cd.png
new file mode 100644
index 0000000..848bdaf
Binary files /dev/null and b/public/icons/page_white_cd.png differ
diff --git a/public/icons/page_white_code.png b/public/icons/page_white_code.png
new file mode 100644
index 0000000..0c76bd1
Binary files /dev/null and b/public/icons/page_white_code.png differ
diff --git a/public/icons/page_white_code_red.png b/public/icons/page_white_code_red.png
new file mode 100644
index 0000000..87a6914
Binary files /dev/null and b/public/icons/page_white_code_red.png differ
diff --git a/public/icons/page_white_coldfusion.png b/public/icons/page_white_coldfusion.png
new file mode 100644
index 0000000..c66011f
Binary files /dev/null and b/public/icons/page_white_coldfusion.png differ
diff --git a/public/icons/page_white_compressed.png b/public/icons/page_white_compressed.png
new file mode 100644
index 0000000..2b6b100
Binary files /dev/null and b/public/icons/page_white_compressed.png differ
diff --git a/public/icons/page_white_copy.png b/public/icons/page_white_copy.png
new file mode 100644
index 0000000..a9f31a2
Binary files /dev/null and b/public/icons/page_white_copy.png differ
diff --git a/public/icons/page_white_cplusplus.png b/public/icons/page_white_cplusplus.png
new file mode 100644
index 0000000..a87cf84
Binary files /dev/null and b/public/icons/page_white_cplusplus.png differ
diff --git a/public/icons/page_white_csharp.png b/public/icons/page_white_csharp.png
new file mode 100644
index 0000000..ffb8fc9
Binary files /dev/null and b/public/icons/page_white_csharp.png differ
diff --git a/public/icons/page_white_cup.png b/public/icons/page_white_cup.png
new file mode 100644
index 0000000..0a7d6f4
Binary files /dev/null and b/public/icons/page_white_cup.png differ
diff --git a/public/icons/page_white_database.png b/public/icons/page_white_database.png
new file mode 100644
index 0000000..bddba1f
Binary files /dev/null and b/public/icons/page_white_database.png differ
diff --git a/public/icons/page_white_delete.png b/public/icons/page_white_delete.png
new file mode 100644
index 0000000..af1ecaf
Binary files /dev/null and b/public/icons/page_white_delete.png differ
diff --git a/public/icons/page_white_dvd.png b/public/icons/page_white_dvd.png
new file mode 100644
index 0000000..4cc537a
Binary files /dev/null and b/public/icons/page_white_dvd.png differ
diff --git a/public/icons/page_white_edit.png b/public/icons/page_white_edit.png
new file mode 100644
index 0000000..b93e776
Binary files /dev/null and b/public/icons/page_white_edit.png differ
diff --git a/public/icons/page_white_error.png b/public/icons/page_white_error.png
new file mode 100644
index 0000000..9fc5a0a
Binary files /dev/null and b/public/icons/page_white_error.png differ
diff --git a/public/icons/page_white_excel.png b/public/icons/page_white_excel.png
new file mode 100644
index 0000000..b977d7e
Binary files /dev/null and b/public/icons/page_white_excel.png differ
diff --git a/public/icons/page_white_find.png b/public/icons/page_white_find.png
new file mode 100644
index 0000000..5818436
Binary files /dev/null and b/public/icons/page_white_find.png differ
diff --git a/public/icons/page_white_flash.png b/public/icons/page_white_flash.png
new file mode 100644
index 0000000..5769120
Binary files /dev/null and b/public/icons/page_white_flash.png differ
diff --git a/public/icons/page_white_freehand.png b/public/icons/page_white_freehand.png
new file mode 100644
index 0000000..8d719df
Binary files /dev/null and b/public/icons/page_white_freehand.png differ
diff --git a/public/icons/page_white_gear.png b/public/icons/page_white_gear.png
new file mode 100644
index 0000000..106f5aa
Binary files /dev/null and b/public/icons/page_white_gear.png differ
diff --git a/public/icons/page_white_get.png b/public/icons/page_white_get.png
new file mode 100644
index 0000000..e4a1ecb
Binary files /dev/null and b/public/icons/page_white_get.png differ
diff --git a/public/icons/page_white_go.png b/public/icons/page_white_go.png
new file mode 100644
index 0000000..7e62a92
Binary files /dev/null and b/public/icons/page_white_go.png differ
diff --git a/public/icons/page_white_h.png b/public/icons/page_white_h.png
new file mode 100644
index 0000000..e902abb
Binary files /dev/null and b/public/icons/page_white_h.png differ
diff --git a/public/icons/page_white_horizontal.png b/public/icons/page_white_horizontal.png
new file mode 100644
index 0000000..1d2d0a4
Binary files /dev/null and b/public/icons/page_white_horizontal.png differ
diff --git a/public/icons/page_white_key.png b/public/icons/page_white_key.png
new file mode 100644
index 0000000..d616484
Binary files /dev/null and b/public/icons/page_white_key.png differ
diff --git a/public/icons/page_white_lightning.png b/public/icons/page_white_lightning.png
new file mode 100644
index 0000000..7215d1e
Binary files /dev/null and b/public/icons/page_white_lightning.png differ
diff --git a/public/icons/page_white_link.png b/public/icons/page_white_link.png
new file mode 100644
index 0000000..bf7bd1c
Binary files /dev/null and b/public/icons/page_white_link.png differ
diff --git a/public/icons/page_white_magnify.png b/public/icons/page_white_magnify.png
new file mode 100644
index 0000000..f6b74cc
Binary files /dev/null and b/public/icons/page_white_magnify.png differ
diff --git a/public/icons/page_white_medal.png b/public/icons/page_white_medal.png
new file mode 100644
index 0000000..d3fffb6
Binary files /dev/null and b/public/icons/page_white_medal.png differ
diff --git a/public/icons/page_white_office.png b/public/icons/page_white_office.png
new file mode 100644
index 0000000..a65bcb3
Binary files /dev/null and b/public/icons/page_white_office.png differ
diff --git a/public/icons/page_white_paint.png b/public/icons/page_white_paint.png
new file mode 100644
index 0000000..23a37b8
Binary files /dev/null and b/public/icons/page_white_paint.png differ
diff --git a/public/icons/page_white_paintbrush.png b/public/icons/page_white_paintbrush.png
new file mode 100644
index 0000000..f907e44
Binary files /dev/null and b/public/icons/page_white_paintbrush.png differ
diff --git a/public/icons/page_white_paste.png b/public/icons/page_white_paste.png
new file mode 100644
index 0000000..5b2cbb3
Binary files /dev/null and b/public/icons/page_white_paste.png differ
diff --git a/public/icons/page_white_php.png b/public/icons/page_white_php.png
new file mode 100644
index 0000000..7868a25
Binary files /dev/null and b/public/icons/page_white_php.png differ
diff --git a/public/icons/page_white_picture.png b/public/icons/page_white_picture.png
new file mode 100644
index 0000000..134b669
Binary files /dev/null and b/public/icons/page_white_picture.png differ
diff --git a/public/icons/page_white_powerpoint.png b/public/icons/page_white_powerpoint.png
new file mode 100644
index 0000000..c4eff03
Binary files /dev/null and b/public/icons/page_white_powerpoint.png differ
diff --git a/public/icons/page_white_put.png b/public/icons/page_white_put.png
new file mode 100644
index 0000000..884ffd6
Binary files /dev/null and b/public/icons/page_white_put.png differ
diff --git a/public/icons/page_white_ruby.png b/public/icons/page_white_ruby.png
new file mode 100644
index 0000000..f59b7c4
Binary files /dev/null and b/public/icons/page_white_ruby.png differ
diff --git a/public/icons/page_white_stack.png b/public/icons/page_white_stack.png
new file mode 100644
index 0000000..44084ad
Binary files /dev/null and b/public/icons/page_white_stack.png differ
diff --git a/public/icons/page_white_star.png b/public/icons/page_white_star.png
new file mode 100644
index 0000000..3a1441c
Binary files /dev/null and b/public/icons/page_white_star.png differ
diff --git a/public/icons/page_white_swoosh.png b/public/icons/page_white_swoosh.png
new file mode 100644
index 0000000..e770829
Binary files /dev/null and b/public/icons/page_white_swoosh.png differ
diff --git a/public/icons/page_white_text.png b/public/icons/page_white_text.png
new file mode 100644
index 0000000..813f712
Binary files /dev/null and b/public/icons/page_white_text.png differ
diff --git a/public/icons/page_white_text_width.png b/public/icons/page_white_text_width.png
new file mode 100644
index 0000000..d9cf132
Binary files /dev/null and b/public/icons/page_white_text_width.png differ
diff --git a/public/icons/page_white_tux.png b/public/icons/page_white_tux.png
new file mode 100644
index 0000000..52699bf
Binary files /dev/null and b/public/icons/page_white_tux.png differ
diff --git a/public/icons/page_white_vector.png b/public/icons/page_white_vector.png
new file mode 100644
index 0000000..4a05955
Binary files /dev/null and b/public/icons/page_white_vector.png differ
diff --git a/public/icons/page_white_visualstudio.png b/public/icons/page_white_visualstudio.png
new file mode 100644
index 0000000..a0a433d
Binary files /dev/null and b/public/icons/page_white_visualstudio.png differ
diff --git a/public/icons/page_white_width.png b/public/icons/page_white_width.png
new file mode 100644
index 0000000..1eb8809
Binary files /dev/null and b/public/icons/page_white_width.png differ
diff --git a/public/icons/page_white_word.png b/public/icons/page_white_word.png
new file mode 100644
index 0000000..ae8ecbf
Binary files /dev/null and b/public/icons/page_white_word.png differ
diff --git a/public/icons/page_white_world.png b/public/icons/page_white_world.png
new file mode 100644
index 0000000..6ed2490
Binary files /dev/null and b/public/icons/page_white_world.png differ
diff --git a/public/icons/page_white_wrench.png b/public/icons/page_white_wrench.png
new file mode 100644
index 0000000..fecadd0
Binary files /dev/null and b/public/icons/page_white_wrench.png differ
diff --git a/public/icons/page_white_zip.png b/public/icons/page_white_zip.png
new file mode 100644
index 0000000..fd4bbcc
Binary files /dev/null and b/public/icons/page_white_zip.png differ
diff --git a/public/icons/page_word.png b/public/icons/page_word.png
new file mode 100644
index 0000000..834cdfa
Binary files /dev/null and b/public/icons/page_word.png differ
diff --git a/public/icons/page_world.png b/public/icons/page_world.png
new file mode 100644
index 0000000..b8895dd
Binary files /dev/null and b/public/icons/page_world.png differ
diff --git a/public/style.css b/public/style.css
new file mode 100644
index 0000000..0709908
--- /dev/null
+++ b/public/style.css
@@ -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*/
+ at 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;
+ }
+}
diff --git a/test/fixtures/#directory/index.html b/test/fixtures/#directory/index.html
new file mode 100644
index 0000000..00a2db4
--- /dev/null
+++ b/test/fixtures/#directory/index.html
@@ -0,0 +1 @@
+<p>tobi, loki, jane</p>
\ No newline at end of file
diff --git a/test/fixtures/.hidden b/test/fixtures/.hidden
new file mode 100644
index 0000000..b885243
--- /dev/null
+++ b/test/fixtures/.hidden
@@ -0,0 +1 @@
+I am hidden
\ No newline at end of file
diff --git a/test/fixtures/collect/sample b/test/fixtures/collect/sample
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/collect/sample.jpg b/test/fixtures/collect/sample.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/collect/sample.mp4 b/test/fixtures/collect/sample.mp4
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/collect/sample.pdf b/test/fixtures/collect/sample.pdf
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/collect/sample.qfx b/test/fixtures/collect/sample.qfx
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/collect/sample.rdf b/test/fixtures/collect/sample.rdf
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/collect/sample.txt b/test/fixtures/collect/sample.txt
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/collect/sample.xlsx b/test/fixtures/collect/sample.xlsx
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/file #1.txt b/test/fixtures/file #1.txt
new file mode 100644
index 0000000..72c0718
--- /dev/null
+++ b/test/fixtures/file #1.txt
@@ -0,0 +1 @@
+#1 file!
\ No newline at end of file
diff --git a/test/fixtures/foo bar b/test/fixtures/foo bar
new file mode 100644
index 0000000..3f95386
--- /dev/null
+++ b/test/fixtures/foo bar
@@ -0,0 +1 @@
+baz
\ No newline at end of file
diff --git a/test/fixtures/g# %3 o %2525 %37 dir/empty.txt b/test/fixtures/g# %3 o %2525 %37 dir/empty.txt
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/nums b/test/fixtures/nums
new file mode 100644
index 0000000..e2e107a
--- /dev/null
+++ b/test/fixtures/nums
@@ -0,0 +1 @@
+123456789
\ No newline at end of file
diff --git a/test/fixtures/todo.txt b/test/fixtures/todo.txt
new file mode 100644
index 0000000..8c3539d
--- /dev/null
+++ b/test/fixtures/todo.txt
@@ -0,0 +1 @@
+- groceries
\ No newline at end of file
diff --git a/test/fixtures/users/index.html b/test/fixtures/users/index.html
new file mode 100644
index 0000000..00a2db4
--- /dev/null
+++ b/test/fixtures/users/index.html
@@ -0,0 +1 @@
+<p>tobi, loki, jane</p>
\ No newline at end of file
diff --git a/test/fixtures/users/tobi.txt b/test/fixtures/users/tobi.txt
new file mode 100644
index 0000000..9d9529d
--- /dev/null
+++ b/test/fixtures/users/tobi.txt
@@ -0,0 +1 @@
+ferret
\ No newline at end of file
diff --git "a/test/fixtures/\343\201\225\343\201\217\343\202\211.txt" "b/test/fixtures/\343\201\225\343\201\217\343\202\211.txt"
new file mode 100644
index 0000000..e69de29
diff --git a/test/shared/index.js b/test/shared/index.js
new file mode 100644
index 0000000..3958366
--- /dev/null
+++ b/test/shared/index.js
@@ -0,0 +1,26 @@
+
+var bytes = require('bytes');
+
+exports['default request body'] = function(app){
+ it('should default to {}', function(done){
+ app.request()
+ .post('/')
+ .end(function(res){
+ res.body.should.equal('{}');
+ done();
+ })
+ })
+};
+
+exports['limit body to'] = function(size, type, app){
+ it('should accept a limit option', function(done){
+ app.request()
+ .post('/')
+ .set('Content-Length', bytes(size) + 1)
+ .set('Content-Type', type)
+ .end(function(res){
+ res.should.have.status(413);
+ done();
+ })
+ })
+}
\ No newline at end of file
diff --git a/test/shared/styles.css b/test/shared/styles.css
new file mode 100644
index 0000000..3310f40
--- /dev/null
+++ b/test/shared/styles.css
@@ -0,0 +1,3 @@
+body {
+ color: #00ff00;
+}
\ No newline at end of file
diff --git a/test/shared/template.html b/test/shared/template.html
new file mode 100644
index 0000000..04ed760
--- /dev/null
+++ b/test/shared/template.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>listing directory {directory}</title>
+ <style>{style}</style>
+ </head>
+ <body class="directory">
+ <h1>This is the test template</h1>
+ <h2>directory {directory}</h2>
+ <div id="wrapper">
+ <h1>{linked-path}</h1>
+ {files}
+ </div>
+ </body>
+</html>
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..c595dc6
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,618 @@
+
+var http = require('http');
+var fs = require('fs');
+var path = require('path');
+var request = require('supertest');
+var should = require('should');
+var serveIndex = require('..');
+
+var fixtures = path.join(__dirname, '/fixtures');
+var relative = path.relative(process.cwd(), fixtures);
+
+var skipRelative = ~relative.indexOf('..') || path.resolve(relative) === relative;
+
+describe('serveIndex(root)', function () {
+ it('should require root', function () {
+ serveIndex.should.throw(/root path required/)
+ })
+
+ it('should serve text/html without Accept header', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .expect('Content-Type', 'text/html; charset=utf-8')
+ .expect(200, done)
+ })
+
+ it('should serve a directory index', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .expect(200, /todo\.txt/, done)
+ })
+
+ it('should work with HEAD requests', function (done) {
+ var server = createServer()
+
+ request(server)
+ .head('/')
+ .expect(200, '', done)
+ })
+
+ it('should work with OPTIONS requests', function (done) {
+ var server = createServer()
+
+ request(server)
+ .options('/')
+ .expect('Allow', 'GET, HEAD, OPTIONS')
+ .expect(200, done)
+ })
+
+ it('should deny POST requests', function (done) {
+ var server = createServer()
+
+ request(server)
+ .post('/')
+ .expect(405, done)
+ })
+
+ it('should deny path will NULL byte', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/%00')
+ .expect(400, done)
+ })
+
+ it('should deny path outside root', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/../')
+ .expect(403, done)
+ })
+
+ it('should skip non-existent paths', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/bogus')
+ .expect(404, 'Not Found', done)
+ })
+
+ it('should treat an ENAMETOOLONG as a 414', function (done) {
+ var path = Array(11000).join('foobar')
+ var server = createServer()
+
+ request(server)
+ .get('/' + path)
+ .expect(414, done)
+ })
+
+ it('should skip non-directories', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/nums')
+ .expect(404, 'Not Found', done)
+ })
+
+ describe('when given Accept: header', function () {
+ describe('when Accept: application/json is given', function () {
+ it('should respond with json', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .set('Accept', 'application/json')
+ .expect('Content-Type', /json/)
+ .expect(/g# %3 o %2525 %37 dir/)
+ .expect(/users/)
+ .expect(/file #1\.txt/)
+ .expect(/nums/)
+ .expect(/todo\.txt/)
+ .expect(/さくら\.txt/)
+ .expect(200, done)
+ });
+ });
+
+ describe('when Accept: text/html is given', function () {
+ it('should respond with html', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect('Content-Type', 'text/html; charset=utf-8')
+ .expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/)
+ .expect(/<a href="\/users"/)
+ .expect(/<a href="\/file%20%231.txt"/)
+ .expect(/<a href="\/todo.txt"/)
+ .expect(/<a href="\/%E3%81%95%E3%81%8F%E3%82%89\.txt"/)
+ .end(done);
+ });
+
+ it('should sort folders first', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect('Content-Type', 'text/html; charset=utf-8')
+ .end(function (err, res) {
+ if (err) throw err;
+ var urls = res.text.split(/<a href="([^"]*)"/).filter(function(s, i){ return i%2; });
+ urls.should.eql([
+ '/%23directory',
+ '/collect',
+ '/g%23%20%253%20o%20%252525%20%2537%20dir',
+ '/users',
+ '/file%20%231.txt',
+ '/foo%20bar',
+ '/nums',
+ '/todo.txt',
+ '/%E3%81%95%E3%81%8F%E3%82%89.txt'
+ ]);
+ done();
+ });
+ });
+ });
+
+ describe('when Accept: text/plain is given', function () {
+ it('should respond with text', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .set('Accept', 'text/plain')
+ .expect(200)
+ .expect('Content-Type', 'text/plain; charset=utf-8')
+ .expect(/users/)
+ .expect(/g# %3 o %2525 %37 dir/)
+ .expect(/file #1.txt/)
+ .expect(/todo.txt/)
+ .expect(/さくら\.txt/)
+ .end(done);
+ });
+ });
+
+ describe('when Accept: application/x-bogus is given', function () {
+ it('should respond with 406', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .set('Accept', 'application/x-bogus')
+ .expect(406, done)
+ });
+ });
+ });
+
+ describe('with "hidden" option', function () {
+ it('should filter hidden files by default', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/')
+ .expect(200, function (err, res) {
+ if (err) return done(err)
+ res.text.should.not.containEql('.hidden')
+ done()
+ });
+ });
+
+ it('should filter hidden files', function (done) {
+ var server = createServer('test/fixtures', {'hidden': false})
+
+ request(server)
+ .get('/')
+ .expect(200, function (err, res) {
+ if (err) return done(err)
+ res.text.should.not.containEql('.hidden')
+ done()
+ });
+ });
+
+ it('should not filter hidden files', function (done) {
+ var server = createServer('test/fixtures', {'hidden': true})
+
+ request(server)
+ .get('/')
+ .expect(200, /\.hidden/, done)
+ });
+ });
+
+ describe('with "filter" option', function () {
+ it('should custom filter files', function (done) {
+ var seen = false
+ var server = createServer(fixtures, {'filter': filter})
+
+ function filter(name) {
+ if (name.indexOf('foo') === -1) return true
+ seen = true
+ return false
+ }
+
+ request(server)
+ .get('/')
+ .expect(200, function (err, res) {
+ if (err) return done(err)
+ seen.should.be.true
+ res.text.should.not.containEql('foo')
+ done()
+ });
+ });
+
+ it('should filter after hidden filter', function (done) {
+ var seen = false
+ var server = createServer(fixtures, {'filter': filter, 'hidden': false})
+
+ function filter(name) {
+ seen = seen || name.indexOf('.') === 0
+ return true
+ }
+
+ request(server)
+ .get('/')
+ .expect(200, function (err, res) {
+ if (err) return done(err)
+ seen.should.be.false
+ done()
+ });
+ });
+
+ it('should filter directory paths', function (done) {
+ var seen = false
+ var server = createServer(fixtures, {'filter': filter})
+
+ function filter(name, index, list, dir) {
+ if (path.normalize(dir) === path.normalize(path.join(fixtures, '/users'))) {
+ seen = true
+ }
+ return true
+ }
+
+ request(server)
+ .get('/users')
+ .expect(200, function (err, res) {
+ if (err) return done(err)
+ seen.should.be.true
+ done()
+ });
+ });
+ });
+
+ describe('with "icons" option', function () {
+ it('should include icons for html', function (done) {
+ var server = createServer(fixtures, {'icons': true})
+
+ request(server)
+ .get('/collect')
+ .expect(/data:image\/png/)
+ .expect(/icon-default/)
+ .expect(/icon-directory/)
+ .expect(/icon-image/)
+ .expect(/icon-txt/)
+ .expect(/icon-application-pdf/)
+ .expect(/icon-video/)
+ .expect(/icon-xml/)
+ .expect(200, done)
+ });
+ });
+
+ describe('when using custom handler', function () {
+ describe('exports.html', function () {
+ var orig = serveIndex.html
+ after(function () {
+ serveIndex.html = orig
+ })
+
+ it('should get called with Accept: text/html', function (done) {
+ var server = createServer()
+
+ serveIndex.html = function (req, res, files) {
+ res.setHeader('Content-Type', 'text/html');
+ res.end('called');
+ }
+
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(200, 'called', done)
+ });
+
+ it('should get file list', function (done) {
+ var server = createServer()
+
+ serveIndex.html = function (req, res, files) {
+ var text = files
+ .filter(function (f) { return /\.txt$/.test(f) })
+ .sort()
+ res.setHeader('Content-Type', 'text/html')
+ res.end('<b>' + text.length + ' text files</b>')
+ }
+
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(200, '<b>3 text files</b>', done)
+ });
+
+ it('should get dir name', function (done) {
+ var server = createServer()
+
+ serveIndex.html = function (req, res, files, next, dir) {
+ res.setHeader('Content-Type', 'text/html')
+ res.end('<b>' + dir + '</b>')
+ }
+
+ request(server)
+ .get('/users/')
+ .set('Accept', 'text/html')
+ .expect(200, '<b>/users/</b>', done)
+ });
+
+ it('should get template path', function (done) {
+ var server = createServer()
+
+ serveIndex.html = function (req, res, files, next, dir, showUp, icons, path, view, template) {
+ res.setHeader('Content-Type', 'text/html')
+ res.end(String(fs.existsSync(template)))
+ }
+
+ request(server)
+ .get('/users/')
+ .set('Accept', 'text/html')
+ .expect(200, 'true', done)
+ });
+
+ it('should get template with tokens', function (done) {
+ var server = createServer()
+
+ serveIndex.html = function (req, res, files, next, dir, showUp, icons, path, view, template) {
+ res.setHeader('Content-Type', 'text/html')
+ res.end(fs.readFileSync(template, 'utf8'))
+ }
+
+ request(server)
+ .get('/users/')
+ .set('Accept', 'text/html')
+ .expect(/{directory}/)
+ .expect(/{files}/)
+ .expect(/{linked-path}/)
+ .expect(/{style}/)
+ .expect(200, done)
+ });
+
+ it('should get stylesheet path', function (done) {
+ var server = createServer()
+
+ serveIndex.html = function (req, res, files, next, dir, showUp, icons, path, view, template, stylesheet) {
+ res.setHeader('Content-Type', 'text/html')
+ res.end(String(fs.existsSync(stylesheet)))
+ }
+
+ request(server)
+ .get('/users/')
+ .set('Accept', 'text/html')
+ .expect(200, 'true', done)
+ });
+ });
+
+ describe('exports.plain', function () {
+ var orig = serveIndex.plain
+ after(function () {
+ serveIndex.plain = orig
+ })
+
+ it('should get called with Accept: text/plain', function (done) {
+ var server = createServer()
+
+ serveIndex.plain = function (req, res, files) {
+ res.setHeader('Content-Type', 'text/plain');
+ res.end('called');
+ }
+
+ request(server)
+ .get('/')
+ .set('Accept', 'text/plain')
+ .expect(200, 'called', done)
+ });
+ });
+
+ describe('exports.json', function () {
+ var orig = serveIndex.json
+ after(function () {
+ serveIndex.json = orig
+ })
+
+ it('should get called with Accept: application/json', function (done) {
+ var server = createServer()
+
+ serveIndex.json = function (req, res, files) {
+ res.setHeader('Content-Type', 'application/json');
+ res.end('"called"');
+ }
+
+ request(server)
+ .get('/')
+ .set('Accept', 'application/json')
+ .expect(200, '"called"', done)
+ });
+ });
+ });
+
+ describe('when navigating to other directory', function () {
+ it('should respond with correct listing', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/users/')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect('Content-Type', 'text/html; charset=utf-8')
+ .expect(/<a href="\/users\/index.html"/)
+ .expect(/<a href="\/users\/tobi.txt"/)
+ .end(done);
+ });
+
+ it('should work for directory with #', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/%23directory/')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect('Content-Type', 'text/html; charset=utf-8')
+ .expect(/<a href="\/%23directory"/)
+ .expect(/<a href="\/%23directory\/index.html"/)
+ .end(done);
+ });
+
+ it('should work for directory with special chars', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/g%23%20%253%20o%20%252525%20%2537%20dir/')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect('Content-Type', 'text/html; charset=utf-8')
+ .expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/)
+ .expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir\/empty.txt"/)
+ .end(done);
+ });
+
+ it('should not work for outside root', function (done) {
+ var server = createServer()
+
+ request(server)
+ .get('/../support/')
+ .set('Accept', 'text/html')
+ .expect(403, done);
+ });
+ });
+
+ describe('when setting a custom template', function () {
+ var server;
+ before(function () {
+ server = createServer(fixtures, {'template': __dirname + '/shared/template.html'});
+ });
+
+ it('should respond with file list', function (done) {
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/)
+ .expect(/<a href="\/users"/)
+ .expect(/<a href="\/file%20%231.txt"/)
+ .expect(/<a href="\/todo.txt"/)
+ .expect(200, done)
+ });
+
+ it('should respond with testing template sentence', function (done) {
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(200, /This is the test template/, done)
+ });
+
+ it('should have default styles', function (done) {
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(200, /ul#files/, done)
+ });
+
+ it('should list directory twice', function (done) {
+ request(server)
+ .get('/users/')
+ .set('Accept', 'text/html')
+ .expect(function (res) {
+ var occurances = res.text.match(/directory \/users\//g)
+ if (occurances && occurances.length === 2) return
+ throw new Error('directory not listed twice')
+ })
+ .expect(200, done)
+ });
+ });
+
+ describe('when setting a custom stylesheet', function () {
+ var server;
+ before(function () {
+ server = createServer(fixtures, {'stylesheet': __dirname + '/shared/styles.css'});
+ });
+
+ it('should respond with appropriate embedded styles', function (done) {
+ request(server)
+ .get('/')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect('Content-Type', 'text/html; charset=utf-8')
+ .expect(/color: #00ff00;/)
+ .end(done);
+ });
+ });
+
+ describe('when set with trailing slash', function () {
+ var server;
+ before(function () {
+ server = createServer(fixtures + '/');
+ });
+
+ it('should respond with file list', function (done) {
+ request(server)
+ .get('/')
+ .set('Accept', 'application/json')
+ .expect('Content-Type', /json/)
+ .expect(/users/)
+ .expect(/file #1\.txt/)
+ .expect(/nums/)
+ .expect(/todo\.txt/)
+ .expect(200, done)
+ });
+ });
+
+ (skipRelative ? describe.skip : describe)('when set to \'.\'', function () {
+ var server;
+ before(function () {
+ server = createServer('.');
+ });
+
+ it('should respond with file list', function (done) {
+ var dest = relative.split(path.sep).join('/');
+ request(server)
+ .get('/' + dest + '/')
+ .set('Accept', 'application/json')
+ .expect('Content-Type', /json/)
+ .expect(/users/)
+ .expect(/file #1\.txt/)
+ .expect(/nums/)
+ .expect(/todo\.txt/)
+ .expect(200, done)
+ });
+
+ it('should not allow serving outside root', function (done) {
+ request(server)
+ .get('/../')
+ .set('Accept', 'text/html')
+ .expect(403, done);
+ });
+ });
+});
+
+function createServer(dir, opts) {
+ dir = dir || fixtures
+
+ var _serveIndex = serveIndex(dir, opts)
+
+ return http.createServer(function (req, res) {
+ _serveIndex(req, res, function (err) {
+ res.statusCode = err ? (err.status || 500) : 404
+ res.end(err ? err.message : 'Not Found')
+ })
+ })
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-serve-index.git
More information about the Pkg-javascript-commits
mailing list