PageRenderTime 27ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/node_modules/serve/node_modules/connect/lib/middleware/directory.js

https://gitlab.com/darkarchon92/portfolio2.0
JavaScript | 227 lines | 111 code | 34 blank | 82 comment | 27 complexity | f3d42c67eeeb00b5a0c67af7b6c0850e MD5 | raw file
  1. /*!
  2. * Connect - directory
  3. * Copyright(c) 2011 Sencha Inc.
  4. * Copyright(c) 2011 TJ Holowaychuk
  5. * MIT Licensed
  6. */
  7. // TODO: icon / style for directories
  8. // TODO: arrow key navigation
  9. // TODO: make icons extensible
  10. /**
  11. * Module dependencies.
  12. */
  13. var fs = require('fs')
  14. , parse = require('url').parse
  15. , utils = require('../utils')
  16. , path = require('path')
  17. , normalize = path.normalize
  18. , extname = path.extname
  19. , join = path.join;
  20. /*!
  21. * Icon cache.
  22. */
  23. var cache = {};
  24. /**
  25. * Directory:
  26. *
  27. * Serve directory listings with the given `root` path.
  28. *
  29. * Options:
  30. *
  31. * - `hidden` display hidden (dot) files. Defaults to false.
  32. * - `icons` display icons. Defaults to false.
  33. * - `filter` Apply this filter function to files. Defaults to false.
  34. *
  35. * @param {String} root
  36. * @param {Object} options
  37. * @return {Function}
  38. * @api public
  39. */
  40. exports = module.exports = function directory(root, options){
  41. options = options || {};
  42. // root required
  43. if (!root) throw new Error('directory() root path required');
  44. var hidden = options.hidden
  45. , icons = options.icons
  46. , filter = options.filter
  47. , root = normalize(root);
  48. return function directory(req, res, next) {
  49. var accept = req.headers.accept || 'text/plain'
  50. , url = parse(req.url)
  51. , dir = decodeURIComponent(url.pathname)
  52. , path = normalize(join(root, dir))
  53. , originalUrl = parse(req.originalUrl)
  54. , originalDir = decodeURIComponent(originalUrl.pathname)
  55. , showUp = path != root && path != root + '/';
  56. // null byte(s), bad request
  57. if (~path.indexOf('\0')) return next(utils.error(400));
  58. // malicious path, forbidden
  59. if (0 != path.indexOf(root)) return next(utils.error(403));
  60. // check if we have a directory
  61. fs.stat(path, function(err, stat){
  62. if (err) return 'ENOENT' == err.code
  63. ? next()
  64. : next(err);
  65. if (!stat.isDirectory()) return next();
  66. // fetch files
  67. fs.readdir(path, function(err, files){
  68. if (err) return next(err);
  69. if (!hidden) files = removeHidden(files);
  70. if (filter) files = files.filter(filter);
  71. files.sort();
  72. // content-negotiation
  73. for (var key in exports) {
  74. if (~accept.indexOf(key) || ~accept.indexOf('*/*')) {
  75. exports[key](req, res, files, next, originalDir, showUp, icons);
  76. return;
  77. }
  78. }
  79. // not acceptable
  80. next(utils.error(406));
  81. });
  82. });
  83. };
  84. };
  85. /**
  86. * Respond with text/html.
  87. */
  88. exports.html = function(req, res, files, next, dir, showUp, icons){
  89. fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){
  90. if (err) return next(err);
  91. fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){
  92. if (err) return next(err);
  93. if (showUp) files.unshift('..');
  94. str = str
  95. .replace('{style}', style)
  96. .replace('{files}', html(files, dir, icons))
  97. .replace('{directory}', dir)
  98. .replace('{linked-path}', htmlPath(dir));
  99. res.setHeader('Content-Type', 'text/html');
  100. res.setHeader('Content-Length', str.length);
  101. res.end(str);
  102. });
  103. });
  104. };
  105. /**
  106. * Respond with application/json.
  107. */
  108. exports.json = function(req, res, files){
  109. files = JSON.stringify(files);
  110. res.setHeader('Content-Type', 'application/json');
  111. res.setHeader('Content-Length', files.length);
  112. res.end(files);
  113. };
  114. /**
  115. * Respond with text/plain.
  116. */
  117. exports.plain = function(req, res, files){
  118. files = files.join('\n') + '\n';
  119. res.setHeader('Content-Type', 'text/plain');
  120. res.setHeader('Content-Length', files.length);
  121. res.end(files);
  122. };
  123. /**
  124. * Map html `dir`, returning a linked path.
  125. */
  126. function htmlPath(dir) {
  127. var curr = [];
  128. return dir.split('/').map(function(part){
  129. curr.push(part);
  130. return '<a href="' + curr.join('/') + '">' + part + '</a>';
  131. }).join(' / ');
  132. }
  133. /**
  134. * Map html `files`, returning an html unordered list.
  135. */
  136. function html(files, dir, useIcons) {
  137. return '<ul id="files">' + files.map(function(file){
  138. var icon = ''
  139. , classes = [];
  140. if (useIcons && '..' != file) {
  141. icon = icons[extname(file)] || icons.default;
  142. icon = '<img src="data:image/png;base64,' + load(icon) + '" />';
  143. classes.push('icon');
  144. }
  145. return '<li><a href="'
  146. + join(dir, file)
  147. + '" class="'
  148. + classes.join(' ') + '"'
  149. + ' title="' + file + '">'
  150. + icon + file + '</a></li>';
  151. }).join('\n') + '</ul>';
  152. }
  153. /**
  154. * Load and cache the given `icon`.
  155. *
  156. * @param {String} icon
  157. * @return {String}
  158. * @api private
  159. */
  160. function load(icon) {
  161. if (cache[icon]) return cache[icon];
  162. return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64');
  163. }
  164. /**
  165. * Filter "hidden" `files`, aka files
  166. * beginning with a `.`.
  167. *
  168. * @param {Array} files
  169. * @return {Array}
  170. * @api private
  171. */
  172. function removeHidden(files) {
  173. return files.filter(function(file){
  174. return '.' != file[0];
  175. });
  176. }
  177. /**
  178. * Icon map.
  179. */
  180. var icons = {
  181. '.js': 'page_white_code_red.png'
  182. , '.c': 'page_white_c.png'
  183. , '.h': 'page_white_h.png'
  184. , '.cc': 'page_white_cplusplus.png'
  185. , '.php': 'page_white_php.png'
  186. , '.rb': 'page_white_ruby.png'
  187. , '.cpp': 'page_white_cplusplus.png'
  188. , '.swf': 'page_white_flash.png'
  189. , '.pdf': 'page_white_acrobat.png'
  190. , 'default': 'page_white.png'
  191. };