Fully end to end encrypted anonymous chat program. Server only stores public key lookup for users and the encrypted messages. No credentials are transfered to the server, but kept in local browser storage. This allows 100% safe chatting. https://safechat.ch
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

256 lines
6.9 KiB

/*!
* Stylus - middleware
* Copyright (c) Automattic <developer.wordpress.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var stylus = require('./stylus')
, fs = require('fs')
, url = require('url')
, dirname = require('path').dirname
, mkdirp = require('mkdirp')
, join = require('path').join
, sep = require('path').sep
, debug = require('debug')('stylus:middleware');
/**
* Import map.
*/
var imports = {};
/**
* Return Connect middleware with the given `options`.
*
* Options:
*
* `force` Always re-compile
* `src` Source directory used to find .styl files,
* a string or function accepting `(path)` of request.
* `dest` Destination directory used to output .css files,
* a string or function accepting `(path)` of request,
* when undefined defaults to `src`.
* `compile` Custom compile function, accepting the arguments
* `(str, path)`.
* `compress` Whether the output .css files should be compressed
* `firebug` Emits debug infos in the generated CSS that can
* be used by the FireStylus Firebug plugin
* `linenos` Emits comments in the generated CSS indicating
* the corresponding Stylus line
* 'sourcemap' Generates a sourcemap in sourcemaps v3 format
*
* Examples:
*
* Here we set up the custom compile function so that we may
* set the `compress` option, or define additional functions.
*
* By default the compile function simply sets the `filename`
* and renders the CSS.
*
* function compile(str, path) {
* return stylus(str)
* .set('filename', path)
* .set('compress', true);
* }
*
* Pass the middleware to Connect, grabbing .styl files from this directory
* and saving .css files to _./public_. Also supplying our custom `compile` function.
*
* Following that we have a `static()` layer setup to serve the .css
* files generated by Stylus.
*
* var app = connect();
*
* app.middleware({
* src: __dirname
* , dest: __dirname + '/public'
* , compile: compile
* })
*
* app.use(connect.static(__dirname + '/public'));
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function(options){
options = options || {};
// Accept src/dest dir
if ('string' == typeof options) {
options = { src: options };
}
// Force compilation
var force = options.force;
// Source dir required
var src = options.src;
if (!src) throw new Error('stylus.middleware() requires "src" directory');
// Default dest dir to source
var dest = options.dest || src;
// Default compile callback
options.compile = options.compile || function(str, path){
// inline sourcemap
if (options.sourcemap) {
if ('boolean' == typeof options.sourcemap)
options.sourcemap = {};
options.sourcemap.inline = true;
}
return stylus(str)
.set('filename', path)
.set('compress', options.compress)
.set('firebug', options.firebug)
.set('linenos', options.linenos)
.set('sourcemap', options.sourcemap);
};
// Middleware
return function stylus(req, res, next){
if ('GET' != req.method && 'HEAD' != req.method) return next();
var path = url.parse(req.url).pathname;
if (/\.css$/.test(path)) {
if (typeof dest == 'string') {
// check for dest-path overlap
var overlap = compare(dest, path).length;
if ('/' == path.charAt(0)) overlap++;
path = path.slice(overlap);
}
var cssPath, stylusPath;
cssPath = (typeof dest == 'function')
? dest(path)
: join(dest, path);
stylusPath = (typeof src == 'function')
? src(path)
: join(src, path.replace('.css', '.styl'));
// Ignore ENOENT to fall through as 404
function error(err) {
next('ENOENT' == err.code
? null
: err);
}
// Force
if (force) return compile();
// Compile to cssPath
function compile() {
debug('read %s', cssPath);
fs.readFile(stylusPath, 'utf8', function(err, str){
if (err) return error(err);
var style = options.compile(str, stylusPath);
var paths = style.options._imports = [];
imports[stylusPath] = null;
style.render(function(err, css){
if (err) return next(err);
debug('render %s', stylusPath);
imports[stylusPath] = paths;
mkdirp(dirname(cssPath), parseInt('0700', 8), function(err){
if (err) return error(err);
fs.writeFile(cssPath, css, 'utf8', next);
});
});
});
}
// Re-compile on server restart, disregarding
// mtimes since we need to map imports
if (!imports[stylusPath]) return compile();
// Compare mtimes
fs.stat(stylusPath, function(err, stylusStats){
if (err) return error(err);
fs.stat(cssPath, function(err, cssStats){
// CSS has not been compiled, compile it!
if (err) {
if ('ENOENT' == err.code) {
debug('not found %s', cssPath);
compile();
} else {
next(err);
}
} else {
// Source has changed, compile it
if (stylusStats.mtime > cssStats.mtime) {
debug('modified %s', cssPath);
compile();
// Already compiled, check imports
} else {
checkImports(stylusPath, function(changed){
if (debug && changed.length) {
changed.forEach(function(path) {
debug('modified import %s', path);
});
}
changed.length ? compile() : next();
});
}
}
});
});
} else {
next();
}
}
};
/**
* Check `path`'s imports to see if they have been altered.
*
* @param {String} path
* @param {Function} fn
* @api private
*/
function checkImports(path, fn) {
var nodes = imports[path];
if (!nodes) return fn();
if (!nodes.length) return fn();
var pending = nodes.length
, changed = [];
nodes.forEach(function(imported){
fs.stat(imported.path, function(err, stat){
// error or newer mtime
if (err || !imported.mtime || stat.mtime > imported.mtime) {
changed.push(imported.path);
}
--pending || fn(changed);
});
});
}
/**
* get the overlaping path from the end of path A, and the begining of path B.
*
* @param {String} pathA
* @param {String} pathB
* @return {String}
* @api private
*/
function compare(pathA, pathB) {
pathA = pathA.split(sep);
pathB = pathB.split('/');
if (!pathA[pathA.length - 1]) pathA.pop();
if (!pathB[0]) pathB.shift();
var overlap = [];
while (pathA[pathA.length - 1] == pathB[0]) {
overlap.push(pathA.pop());
pathB.shift();
}
return overlap.join('/');
}