@wwawing/serve-index 3.12.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +11 -0
  3. package/index.js +710 -0
  4. package/package.json +42 -0
  5. package/public/directory.html +82 -0
  6. package/public/icons/application_xp.png +0 -0
  7. package/public/icons/application_xp_terminal.png +0 -0
  8. package/public/icons/box.png +0 -0
  9. package/public/icons/cd.png +0 -0
  10. package/public/icons/controller.png +0 -0
  11. package/public/icons/drive.png +0 -0
  12. package/public/icons/film.png +0 -0
  13. package/public/icons/folder.png +0 -0
  14. package/public/icons/font.png +0 -0
  15. package/public/icons/image.png +0 -0
  16. package/public/icons/map.png +0 -0
  17. package/public/icons/page.png +0 -0
  18. package/public/icons/page_add.png +0 -0
  19. package/public/icons/page_attach.png +0 -0
  20. package/public/icons/page_code.png +0 -0
  21. package/public/icons/page_copy.png +0 -0
  22. package/public/icons/page_delete.png +0 -0
  23. package/public/icons/page_edit.png +0 -0
  24. package/public/icons/page_error.png +0 -0
  25. package/public/icons/page_excel.png +0 -0
  26. package/public/icons/page_find.png +0 -0
  27. package/public/icons/page_gear.png +0 -0
  28. package/public/icons/page_go.png +0 -0
  29. package/public/icons/page_green.png +0 -0
  30. package/public/icons/page_key.png +0 -0
  31. package/public/icons/page_lightning.png +0 -0
  32. package/public/icons/page_link.png +0 -0
  33. package/public/icons/page_paintbrush.png +0 -0
  34. package/public/icons/page_paste.png +0 -0
  35. package/public/icons/page_red.png +0 -0
  36. package/public/icons/page_refresh.png +0 -0
  37. package/public/icons/page_save.png +0 -0
  38. package/public/icons/page_white.png +0 -0
  39. package/public/icons/page_white_acrobat.png +0 -0
  40. package/public/icons/page_white_actionscript.png +0 -0
  41. package/public/icons/page_white_add.png +0 -0
  42. package/public/icons/page_white_c.png +0 -0
  43. package/public/icons/page_white_camera.png +0 -0
  44. package/public/icons/page_white_cd.png +0 -0
  45. package/public/icons/page_white_code.png +0 -0
  46. package/public/icons/page_white_code_red.png +0 -0
  47. package/public/icons/page_white_coldfusion.png +0 -0
  48. package/public/icons/page_white_compressed.png +0 -0
  49. package/public/icons/page_white_copy.png +0 -0
  50. package/public/icons/page_white_cplusplus.png +0 -0
  51. package/public/icons/page_white_csharp.png +0 -0
  52. package/public/icons/page_white_cup.png +0 -0
  53. package/public/icons/page_white_database.png +0 -0
  54. package/public/icons/page_white_delete.png +0 -0
  55. package/public/icons/page_white_dvd.png +0 -0
  56. package/public/icons/page_white_edit.png +0 -0
  57. package/public/icons/page_white_error.png +0 -0
  58. package/public/icons/page_white_excel.png +0 -0
  59. package/public/icons/page_white_find.png +0 -0
  60. package/public/icons/page_white_flash.png +0 -0
  61. package/public/icons/page_white_freehand.png +0 -0
  62. package/public/icons/page_white_gear.png +0 -0
  63. package/public/icons/page_white_get.png +0 -0
  64. package/public/icons/page_white_go.png +0 -0
  65. package/public/icons/page_white_h.png +0 -0
  66. package/public/icons/page_white_horizontal.png +0 -0
  67. package/public/icons/page_white_key.png +0 -0
  68. package/public/icons/page_white_lightning.png +0 -0
  69. package/public/icons/page_white_link.png +0 -0
  70. package/public/icons/page_white_magnify.png +0 -0
  71. package/public/icons/page_white_medal.png +0 -0
  72. package/public/icons/page_white_office.png +0 -0
  73. package/public/icons/page_white_paint.png +0 -0
  74. package/public/icons/page_white_paintbrush.png +0 -0
  75. package/public/icons/page_white_paste.png +0 -0
  76. package/public/icons/page_white_php.png +0 -0
  77. package/public/icons/page_white_picture.png +0 -0
  78. package/public/icons/page_white_powerpoint.png +0 -0
  79. package/public/icons/page_white_put.png +0 -0
  80. package/public/icons/page_white_ruby.png +0 -0
  81. package/public/icons/page_white_stack.png +0 -0
  82. package/public/icons/page_white_star.png +0 -0
  83. package/public/icons/page_white_swoosh.png +0 -0
  84. package/public/icons/page_white_text.png +0 -0
  85. package/public/icons/page_white_text_width.png +0 -0
  86. package/public/icons/page_white_tux.png +0 -0
  87. package/public/icons/page_white_vector.png +0 -0
  88. package/public/icons/page_white_visualstudio.png +0 -0
  89. package/public/icons/page_white_width.png +0 -0
  90. package/public/icons/page_white_word.png +0 -0
  91. package/public/icons/page_white_world.png +0 -0
  92. package/public/icons/page_white_wrench.png +0 -0
  93. package/public/icons/page_white_zip.png +0 -0
  94. package/public/icons/page_word.png +0 -0
  95. package/public/icons/page_world.png +0 -0
  96. package/public/style.css +257 -0
  97. package/types/LICENSE +21 -0
  98. package/types/index.d.ts +44 -0
package/index.js ADDED
@@ -0,0 +1,710 @@
1
+ /*!
2
+ * serve-index (WWA Wing Edition)
3
+ * Copyright(c) 2011 Sencha Inc.
4
+ * Copyright(c) 2011 TJ Holowaychuk
5
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
6
+ * Copyright(c) 2015-2024 WWA Wing Team
7
+ * MIT Licensed
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ /**
13
+ * Module dependencies.
14
+ * @private
15
+ */
16
+
17
+ var accepts = require('accepts');
18
+ var createError = require('http-errors');
19
+ var debug = require('debug')('serve-index');
20
+ var escapeHtml = require('escape-html');
21
+ var fs = require('fs')
22
+ , path = require('path')
23
+ , normalize = path.normalize
24
+ , sep = path.sep
25
+ , extname = path.extname
26
+ , join = path.join;
27
+ var Batch = require('batch');
28
+ var mime = require('mime-types');
29
+ var parseUrl = require('parseurl');
30
+ var resolve = require('path').resolve;
31
+ var cssEscape = require('css.escape');
32
+
33
+ /**
34
+ * Module exports.
35
+ * @public
36
+ */
37
+
38
+ module.exports = serveIndex;
39
+
40
+ /*!
41
+ * Icon cache.
42
+ */
43
+
44
+ var cache = {};
45
+
46
+ /*!
47
+ * Default template.
48
+ */
49
+
50
+ var defaultTemplate = join(__dirname, 'public', 'directory.html');
51
+
52
+ /*!
53
+ * Stylesheet.
54
+ */
55
+
56
+ var defaultStylesheet = join(__dirname, 'public', 'style.css');
57
+
58
+ /**
59
+ * Media types and the map for content negotiation.
60
+ */
61
+
62
+ var mediaTypes = [
63
+ 'text/html',
64
+ 'text/plain',
65
+ 'application/json'
66
+ ];
67
+
68
+ var mediaType = {
69
+ 'text/html': 'html',
70
+ 'text/plain': 'plain',
71
+ 'application/json': 'json'
72
+ };
73
+
74
+ /**
75
+ * Serve directory listings with the given `root` path.
76
+ *
77
+ * See Readme.md for documentation of options.
78
+ *
79
+ * @param {String} root
80
+ * @param {Object} options
81
+ * @return {Function} middleware
82
+ * @public
83
+ */
84
+
85
+ function serveIndex(root, options) {
86
+ var opts = options || {};
87
+
88
+ // root required
89
+ if (!root) {
90
+ throw new TypeError('serveIndex() root path required');
91
+ }
92
+
93
+ // resolve root to absolute and normalize
94
+ var rootPath = normalize(resolve(root) + sep);
95
+
96
+ var filter = opts.filter;
97
+ var hidden = opts.hidden;
98
+ var icons = opts.icons;
99
+ var stylesheet = opts.stylesheet || defaultStylesheet;
100
+ var template = opts.template || defaultTemplate;
101
+ var view = opts.view || 'tiles';
102
+ var templateParseMode = opts.templateParseMode || "fileName";
103
+ var iconLoadCallback = opts.iconLoadCallback || ((icon) => fs.readFileSync(__dirname + '/public/icons/' + icon, 'base64'));
104
+
105
+ return function (req, res, next) {
106
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
107
+ res.statusCode = 'OPTIONS' === req.method ? 200 : 405;
108
+ res.setHeader('Allow', 'GET, HEAD, OPTIONS');
109
+ res.setHeader('Content-Length', '0');
110
+ res.end();
111
+ return;
112
+ }
113
+
114
+ // get dir
115
+ var dir = getRequestedDir(req)
116
+
117
+ // bad request
118
+ if (dir === null) return next(createError(400))
119
+
120
+ // parse URLs
121
+ var originalUrl = parseUrl.original(req);
122
+ var originalDir = decodeURIComponent(originalUrl.pathname);
123
+
124
+ // join / normalize from root dir
125
+ var path = normalize(join(rootPath, dir));
126
+
127
+ // null byte(s), bad request
128
+ if (~path.indexOf('\0')) return next(createError(400));
129
+
130
+ // malicious path
131
+ if ((path + sep).substr(0, rootPath.length) !== rootPath) {
132
+ debug('malicious path "%s"', path);
133
+ return next(createError(403));
134
+ }
135
+
136
+ // determine ".." display
137
+ var showUp = normalize(resolve(path) + sep) !== rootPath;
138
+
139
+ // check if we have a directory
140
+ debug('stat "%s"', path);
141
+ fs.stat(path, function(err, stat){
142
+ if (err && err.code === 'ENOENT') {
143
+ return next();
144
+ }
145
+
146
+ if (err) {
147
+ err.status = err.code === 'ENAMETOOLONG'
148
+ ? 414
149
+ : 500;
150
+ return next(err);
151
+ }
152
+
153
+ if (!stat.isDirectory()) return next();
154
+
155
+ // fetch files
156
+ debug('readdir "%s"', path);
157
+ fs.readdir(path, function(err, files){
158
+ if (err) return next(err);
159
+ if (!hidden) files = removeHidden(files);
160
+ if (filter) files = files.filter(function(filename, index, list) {
161
+ return filter(filename, index, list, path);
162
+ });
163
+ files.sort();
164
+
165
+ // content-negotiation
166
+ var accept = accepts(req);
167
+ var type = accept.type(mediaTypes);
168
+
169
+ // not acceptable
170
+ if (!type) return next(createError(406));
171
+ serveIndex[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet, templateParseMode, iconLoadCallback);
172
+ });
173
+ });
174
+ };
175
+ };
176
+
177
+ /**
178
+ * Respond with text/html.
179
+ */
180
+
181
+ serveIndex.html = function _html(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet, templateParseMode, iconLoadCallback) {
182
+ var render = typeof template !== 'function'
183
+ ? createHtmlRender(template, templateParseMode, iconLoadCallback)
184
+ : template
185
+
186
+ if (showUp) {
187
+ files.unshift('..');
188
+ }
189
+
190
+ // stat all files
191
+ stat(path, files, function (err, fileList) {
192
+ if (err) return next(err);
193
+
194
+ // sort file list
195
+ fileList.sort(fileSort);
196
+
197
+ // create locals for rendering
198
+ const locals = {
199
+ directory: dir,
200
+ displayIcons: Boolean(icons),
201
+ fileList: fileList,
202
+ path: path,
203
+ viewName: view
204
+ };
205
+
206
+ if (templateParseMode === "fileName") {
207
+ // read stylesheet
208
+ fs.readFile(stylesheet, 'utf8', function (err, style) {
209
+ if (err) return next(err);
210
+ // render html
211
+ render({...locals, style }, function (err, body) {
212
+ if (err) return next(err);
213
+ send(res, 'text/html', body)
214
+ });
215
+ });
216
+ } else if(templateParseMode === "fileContent") {
217
+ render({...locals, style: stylesheet }, function (err, body) {
218
+ if (err) return next(err);
219
+ send(res, 'text/html', body)
220
+ });
221
+ } else {
222
+ throw new TypeError("templateParseMode is invalid", templateParseMode)
223
+ }
224
+ });
225
+ };
226
+
227
+ /**
228
+ * Respond with application/json.
229
+ */
230
+
231
+ serveIndex.json = function _json (req, res, files, next, dir, showUp, icons, path) {
232
+ // stat all files
233
+ stat(path, files, function (err, fileList) {
234
+ if (err) return next(err)
235
+
236
+ // sort file list
237
+ fileList.sort(fileSort)
238
+
239
+ // serialize
240
+ var body = JSON.stringify(fileList.map(function (file) {
241
+ return file.name
242
+ }))
243
+
244
+ send(res, 'application/json', body)
245
+ })
246
+ };
247
+
248
+ /**
249
+ * Respond with text/plain.
250
+ */
251
+
252
+ serveIndex.plain = function _plain (req, res, files, next, dir, showUp, icons, path) {
253
+ // stat all files
254
+ stat(path, files, function (err, fileList) {
255
+ if (err) return next(err)
256
+
257
+ // sort file list
258
+ fileList.sort(fileSort)
259
+
260
+ // serialize
261
+ var body = fileList.map(function (file) {
262
+ return file.name
263
+ }).join('\n') + '\n'
264
+
265
+ send(res, 'text/plain', body)
266
+ })
267
+ };
268
+
269
+ /**
270
+ * Map html `files`, returning an html unordered list.
271
+ * @private
272
+ */
273
+
274
+ function createHtmlFileList(files, dir, useIcons, view) {
275
+ var html = '<ul id="files" class="view-' + escapeHtml(view) + '">'
276
+ + (view === 'details' ? (
277
+ '<li class="header">'
278
+ + '<span class="name">Name</span>'
279
+ + '<span class="size">Size</span>'
280
+ + '<span class="date">Modified</span>'
281
+ + '</li>') : '');
282
+
283
+ html += files.map(function (file) {
284
+ var classes = [];
285
+ var isDir = file.stat && file.stat.isDirectory();
286
+ var path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
287
+
288
+ if (useIcons) {
289
+ classes.push('icon');
290
+
291
+ if (isDir) {
292
+ classes.push('icon-directory');
293
+ } else {
294
+ var ext = extname(file.name);
295
+ var icon = iconLookup(file.name);
296
+
297
+ classes.push('icon');
298
+ classes.push('icon-' + ext.substring(1));
299
+
300
+ if (classes.indexOf(icon.className) === -1) {
301
+ classes.push(icon.className);
302
+ }
303
+ }
304
+ }
305
+
306
+ path.push(encodeURIComponent(file.name));
307
+
308
+ var date = file.stat && file.name !== '..'
309
+ ? file.stat.mtime.toLocaleDateString() + ' ' + file.stat.mtime.toLocaleTimeString()
310
+ : '';
311
+ var size = file.stat && !isDir
312
+ ? file.stat.size
313
+ : '';
314
+
315
+ return '<li><a href="'
316
+ + escapeHtml(normalizeSlashes(normalize(path.join('/'))))
317
+ + '" class="' + escapeHtml(classes.join(' ')) + '"'
318
+ + ' title="' + escapeHtml(file.name) + '">'
319
+ + '<span class="name">' + escapeHtml(file.name) + '</span>'
320
+ + '<span class="size">' + escapeHtml(size) + '</span>'
321
+ + '<span class="date">' + escapeHtml(date) + '</span>'
322
+ + '</a></li>';
323
+ }).join('\n');
324
+
325
+ html += '</ul>';
326
+
327
+ return html;
328
+ }
329
+
330
+ /**
331
+ * Create function to render html.
332
+ */
333
+
334
+ function createHtmlRender(template, templateParseMode, iconLoadCallback) {
335
+ return function render(locals, callback) {
336
+ if (templateParseMode === "fileName") {
337
+ // read template
338
+ fs.readFile(template, 'utf8', function (err, str) {
339
+ if (err) return callback(err);
340
+ callback(null, parseTemplate(str, locals, iconLoadCallback));
341
+ });
342
+ } else if (templateParseMode === "fileContent") {
343
+ callback(null, parseTemplate(template, locals, iconLoadCallback))
344
+ } else {
345
+ throw new TypeError("templateParseMode is invalid", templateParseMode)
346
+ }
347
+ };
348
+ }
349
+
350
+ function parseTemplate(str, locals, iconLoadCallback) {
351
+ return str
352
+ .replace(/\{style\}/g, locals.style.concat(iconStyle(locals.fileList, locals.displayIcons, iconLoadCallback)))
353
+ .replace(/\{files\}/g, createHtmlFileList(locals.fileList, locals.directory, locals.displayIcons, locals.viewName))
354
+ .replace(/\{directory\}/g, escapeHtml(locals.directory))
355
+ .replace(/\{linked-path\}/g, htmlPath(locals.directory));
356
+ }
357
+
358
+ /**
359
+ * Sort function for with directories first.
360
+ */
361
+
362
+ function fileSort(a, b) {
363
+ // sort ".." to the top
364
+ if (a.name === '..' || b.name === '..') {
365
+ return a.name === b.name ? 0
366
+ : a.name === '..' ? -1 : 1;
367
+ }
368
+
369
+ return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) ||
370
+ String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase());
371
+ }
372
+
373
+ /**
374
+ * Get the requested directory from request.
375
+ *
376
+ * @param req
377
+ * @return {string}
378
+ * @api private
379
+ */
380
+
381
+ function getRequestedDir (req) {
382
+ try {
383
+ return decodeURIComponent(parseUrl(req).pathname)
384
+ } catch (e) {
385
+ return null
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Map html `dir`, returning a linked path.
391
+ */
392
+
393
+ function htmlPath(dir) {
394
+ var parts = dir.split('/');
395
+ var crumb = new Array(parts.length);
396
+
397
+ for (var i = 0; i < parts.length; i++) {
398
+ var part = parts[i];
399
+
400
+ if (part) {
401
+ parts[i] = encodeURIComponent(part);
402
+ crumb[i] = '<a href="' + escapeHtml(parts.slice(0, i + 1).join('/')) + '">' + escapeHtml(part) + '</a>';
403
+ }
404
+ }
405
+
406
+ return crumb.join(' / ');
407
+ }
408
+
409
+ /**
410
+ * Get the icon data for the file name.
411
+ */
412
+
413
+ function iconLookup(filename) {
414
+ var ext = extname(filename);
415
+
416
+ // try by extension
417
+ if (icons[ext]) {
418
+ return {
419
+ className: 'icon-' + ext.substring(1),
420
+ fileName: icons[ext]
421
+ };
422
+ }
423
+
424
+ var mimetype = mime.lookup(ext);
425
+
426
+ // default if no mime type
427
+ if (mimetype === false) {
428
+ return {
429
+ className: 'icon-default',
430
+ fileName: icons.default
431
+ };
432
+ }
433
+
434
+ // try by mime type
435
+ if (icons[mimetype]) {
436
+ return {
437
+ className: 'icon-' + mimetype.replace('/', '-').replace('+', '_'),
438
+ fileName: icons[mimetype]
439
+ };
440
+ }
441
+
442
+ var suffix = mimetype.split('+')[1];
443
+
444
+ if (suffix && icons['+' + suffix]) {
445
+ return {
446
+ className: 'icon-' + suffix,
447
+ fileName: icons['+' + suffix]
448
+ };
449
+ }
450
+
451
+ var type = mimetype.split('/')[0];
452
+
453
+ // try by type only
454
+ if (icons[type]) {
455
+ return {
456
+ className: 'icon-' + type,
457
+ fileName: icons[type]
458
+ };
459
+ }
460
+
461
+ return {
462
+ className: 'icon-default',
463
+ fileName: icons.default
464
+ };
465
+ }
466
+
467
+ /**
468
+ * Load icon images, return css string.
469
+ */
470
+
471
+ function iconStyle(files, useIcons, iconLoadCallback) {
472
+ if (!useIcons) return '';
473
+ var i;
474
+ var list = [];
475
+ var rules = {};
476
+ var selector;
477
+ var selectors = {};
478
+ var style = '';
479
+
480
+ for (i = 0; i < files.length; i++) {
481
+ var file = files[i];
482
+
483
+ var isDir = file.stat && file.stat.isDirectory();
484
+ var icon = isDir
485
+ ? { className: 'icon-directory', fileName: icons.folder }
486
+ : iconLookup(file.name);
487
+ var iconName = icon.fileName;
488
+
489
+ selector = '#files .' + icon.className + ' .name';
490
+
491
+ if (!rules[iconName]) {
492
+ rules[iconName] = 'background-image: url(data:image/png;base64,' + cssEscape(load(iconName, iconLoadCallback)) + ');'
493
+ selectors[iconName] = [];
494
+ list.push(iconName);
495
+ }
496
+
497
+ if (selectors[iconName].indexOf(selector) === -1) {
498
+ selectors[iconName].push(selector);
499
+ }
500
+ }
501
+
502
+ for (i = 0; i < list.length; i++) {
503
+ iconName = list[i];
504
+ style += selectors[iconName].join(',\n') + ' {\n ' + rules[iconName] + '\n}\n';
505
+ }
506
+
507
+ return style;
508
+ }
509
+
510
+ /**
511
+ * Load and cache the given `icon`.
512
+ *
513
+ * @param {String} icon
514
+ * @return {String}
515
+ * @api private
516
+ */
517
+
518
+ function load(icon, iconLoadCallback) {
519
+ if (cache[icon]) return cache[icon];
520
+ return cache[icon] = iconLoadCallback(icon)
521
+ }
522
+
523
+ /**
524
+ * Normalizes the path separator from system separator
525
+ * to URL separator, aka `/`.
526
+ *
527
+ * @param {String} path
528
+ * @return {String}
529
+ * @api private
530
+ */
531
+
532
+ function normalizeSlashes(path) {
533
+ return path.split(sep).join('/');
534
+ };
535
+
536
+ /**
537
+ * Filter "hidden" `files`, aka files
538
+ * beginning with a `.`.
539
+ *
540
+ * @param {Array} files
541
+ * @return {Array}
542
+ * @api private
543
+ */
544
+
545
+ function removeHidden(files) {
546
+ return files.filter(function(file){
547
+ return file[0] !== '.'
548
+ });
549
+ }
550
+
551
+ /**
552
+ * Send a response.
553
+ * @private
554
+ */
555
+
556
+ function send (res, type, body) {
557
+ // security header for content sniffing
558
+ res.setHeader('X-Content-Type-Options', 'nosniff')
559
+
560
+ // standard headers
561
+ res.setHeader('Content-Type', type + '; charset=utf-8')
562
+ res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
563
+
564
+ // body
565
+ res.end(body, 'utf8')
566
+ }
567
+
568
+ /**
569
+ * Stat all files and return array of objects in the form
570
+ * `{ name, stat }`.
571
+ *
572
+ * @param {Array} files
573
+ * @return {Array}
574
+ * @api private
575
+ */
576
+
577
+ function stat(dir, files, cb) {
578
+ var batch = new Batch();
579
+
580
+ batch.concurrency(10);
581
+
582
+ files.forEach(function(file){
583
+ batch.push(function(done){
584
+ fs.stat(join(dir, file), function(err, stat){
585
+ if (err && err.code !== 'ENOENT') return done(err);
586
+
587
+ // pass ENOENT as null stat, not error
588
+ done(null, {
589
+ name: file,
590
+ stat: stat || null
591
+ })
592
+ });
593
+ });
594
+ });
595
+
596
+ batch.end(cb);
597
+ }
598
+
599
+ /**
600
+ * Icon map.
601
+ */
602
+
603
+ var icons = {
604
+ // base icons
605
+ 'default': 'page_white.png',
606
+ 'folder': 'folder.png',
607
+
608
+ // generic mime type icons
609
+ 'font': 'font.png',
610
+ 'image': 'image.png',
611
+ 'text': 'page_white_text.png',
612
+ 'video': 'film.png',
613
+
614
+ // generic mime suffix icons
615
+ '+json': 'page_white_code.png',
616
+ '+xml': 'page_white_code.png',
617
+ '+zip': 'box.png',
618
+
619
+ // specific mime type icons
620
+ 'application/javascript': 'page_white_code_red.png',
621
+ 'application/json': 'page_white_code.png',
622
+ 'application/msword': 'page_white_word.png',
623
+ 'application/pdf': 'page_white_acrobat.png',
624
+ 'application/postscript': 'page_white_vector.png',
625
+ 'application/rtf': 'page_white_word.png',
626
+ 'application/vnd.ms-excel': 'page_white_excel.png',
627
+ 'application/vnd.ms-powerpoint': 'page_white_powerpoint.png',
628
+ 'application/vnd.oasis.opendocument.presentation': 'page_white_powerpoint.png',
629
+ 'application/vnd.oasis.opendocument.spreadsheet': 'page_white_excel.png',
630
+ 'application/vnd.oasis.opendocument.text': 'page_white_word.png',
631
+ 'application/x-7z-compressed': 'box.png',
632
+ 'application/x-sh': 'application_xp_terminal.png',
633
+ 'application/x-msaccess': 'page_white_database.png',
634
+ 'application/x-shockwave-flash': 'page_white_flash.png',
635
+ 'application/x-sql': 'page_white_database.png',
636
+ 'application/x-tar': 'box.png',
637
+ 'application/x-xz': 'box.png',
638
+ 'application/xml': 'page_white_code.png',
639
+ 'application/zip': 'box.png',
640
+ 'image/svg+xml': 'page_white_vector.png',
641
+ 'text/css': 'page_white_code.png',
642
+ 'text/html': 'page_white_code.png',
643
+ 'text/less': 'page_white_code.png',
644
+
645
+ // other, extension-specific icons
646
+ '.accdb': 'page_white_database.png',
647
+ '.apk': 'box.png',
648
+ '.app': 'application_xp.png',
649
+ '.as': 'page_white_actionscript.png',
650
+ '.asp': 'page_white_code.png',
651
+ '.aspx': 'page_white_code.png',
652
+ '.bat': 'application_xp_terminal.png',
653
+ '.bz2': 'box.png',
654
+ '.c': 'page_white_c.png',
655
+ '.cab': 'box.png',
656
+ '.cfm': 'page_white_coldfusion.png',
657
+ '.clj': 'page_white_code.png',
658
+ '.cc': 'page_white_cplusplus.png',
659
+ '.cgi': 'application_xp_terminal.png',
660
+ '.cpp': 'page_white_cplusplus.png',
661
+ '.cs': 'page_white_csharp.png',
662
+ '.db': 'page_white_database.png',
663
+ '.dbf': 'page_white_database.png',
664
+ '.deb': 'box.png',
665
+ '.dll': 'page_white_gear.png',
666
+ '.dmg': 'drive.png',
667
+ '.docx': 'page_white_word.png',
668
+ '.erb': 'page_white_ruby.png',
669
+ '.exe': 'application_xp.png',
670
+ '.fnt': 'font.png',
671
+ '.gam': 'controller.png',
672
+ '.gz': 'box.png',
673
+ '.h': 'page_white_h.png',
674
+ '.ini': 'page_white_gear.png',
675
+ '.iso': 'cd.png',
676
+ '.jar': 'box.png',
677
+ '.java': 'page_white_cup.png',
678
+ '.jsp': 'page_white_cup.png',
679
+ '.lua': 'page_white_code.png',
680
+ '.lz': 'box.png',
681
+ '.lzma': 'box.png',
682
+ '.m': 'page_white_code.png',
683
+ '.map': 'map.png',
684
+ '.msi': 'box.png',
685
+ '.mv4': 'film.png',
686
+ '.pdb': 'page_white_database.png',
687
+ '.php': 'page_white_php.png',
688
+ '.pl': 'page_white_code.png',
689
+ '.pkg': 'box.png',
690
+ '.pptx': 'page_white_powerpoint.png',
691
+ '.psd': 'page_white_picture.png',
692
+ '.py': 'page_white_code.png',
693
+ '.rar': 'box.png',
694
+ '.rb': 'page_white_ruby.png',
695
+ '.rm': 'film.png',
696
+ '.rom': 'controller.png',
697
+ '.rpm': 'box.png',
698
+ '.sass': 'page_white_code.png',
699
+ '.sav': 'controller.png',
700
+ '.scss': 'page_white_code.png',
701
+ '.srt': 'page_white_text.png',
702
+ '.tbz2': 'box.png',
703
+ '.tgz': 'box.png',
704
+ '.tlz': 'box.png',
705
+ '.vb': 'page_white_code.png',
706
+ '.vbs': 'page_white_code.png',
707
+ '.xcf': 'page_white_picture.png',
708
+ '.xlsx': 'page_white_excel.png',
709
+ '.yaws': 'page_white_code.png'
710
+ };