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
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('/'); |
|
}
|
|
|