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