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.
810 lines
18 KiB
810 lines
18 KiB
#!/usr/bin/env node |
|
|
|
/** |
|
* Module dependencies. |
|
*/ |
|
|
|
var fs = require('fs') |
|
, stylus = require('../lib/stylus') |
|
, basename = require('path').basename |
|
, dirname = require('path').dirname |
|
, extname = require('path').extname |
|
, resolve = require('path').resolve |
|
, join = require('path').join |
|
, isWindows = process.platform === 'win32'; |
|
|
|
/** |
|
* Arguments. |
|
*/ |
|
|
|
var args = process.argv.slice(2); |
|
|
|
/** |
|
* Compare flag. |
|
*/ |
|
|
|
var compare = false; |
|
|
|
/** |
|
* Compress flag. |
|
*/ |
|
|
|
var compress = false; |
|
|
|
/** |
|
* CSS conversion flag. |
|
*/ |
|
|
|
var convertCSS = false; |
|
|
|
/** |
|
* Line numbers flag. |
|
*/ |
|
|
|
var linenos = false; |
|
|
|
/** |
|
* CSS class prefix. |
|
*/ |
|
var prefix = ''; |
|
|
|
/** |
|
* Print to stdout flag. |
|
*/ |
|
var print = false; |
|
|
|
/** |
|
* Firebug flag |
|
*/ |
|
|
|
var firebug = false; |
|
|
|
/** |
|
* Sourcemap flag |
|
*/ |
|
|
|
var sourcemap = false; |
|
|
|
/** |
|
* Files to processes. |
|
*/ |
|
|
|
var files = []; |
|
|
|
/** |
|
* Import paths. |
|
*/ |
|
|
|
var paths = []; |
|
|
|
/** |
|
* Destination directory. |
|
*/ |
|
|
|
var dest; |
|
|
|
/** |
|
* Watcher hash. |
|
*/ |
|
|
|
var watchers; |
|
|
|
/** |
|
* Enable REPL. |
|
*/ |
|
|
|
var interactive; |
|
|
|
/** |
|
* Plugins. |
|
*/ |
|
|
|
var plugins = []; |
|
|
|
/** |
|
* Optional url() function. |
|
*/ |
|
|
|
var urlFunction = false; |
|
|
|
/** |
|
* Include CSS on import. |
|
*/ |
|
|
|
var includeCSS = false; |
|
|
|
/** |
|
* Set file imports. |
|
*/ |
|
|
|
var imports = []; |
|
|
|
/** |
|
* Resolve relative urls |
|
*/ |
|
|
|
var resolveURL = false; |
|
|
|
/** |
|
* Disable cache. |
|
*/ |
|
|
|
var disableCache = false; |
|
|
|
/** |
|
* Display dependencies flag. |
|
*/ |
|
|
|
var deps = false; |
|
|
|
/** |
|
* Hoist at-rules. |
|
*/ |
|
|
|
var hoist = false; |
|
|
|
/** |
|
* Usage docs. |
|
*/ |
|
|
|
var usage = [ |
|
'' |
|
, ' Usage: stylus [options] [command] [< in [> out]]' |
|
, ' [file|dir ...]' |
|
, '' |
|
, ' Commands:' |
|
, '' |
|
, ' help [<type>:]<prop> Opens help info at MDN for <prop> in' |
|
, ' your default browser. Optionally' |
|
, ' searches other resources of <type>:' |
|
, ' safari opera w3c ms caniuse quirksmode' |
|
, '' |
|
, ' Options:' |
|
, '' |
|
, ' -i, --interactive Start interactive REPL' |
|
, ' -u, --use <path> Utilize the Stylus plugin at <path>' |
|
, ' -U, --inline Utilize image inlining via data URI support' |
|
, ' -w, --watch Watch file(s) for changes and re-compile' |
|
, ' -o, --out <dir> Output to <dir> when passing files' |
|
, ' -C, --css <src> [dest] Convert CSS input to Stylus' |
|
, ' -I, --include <path> Add <path> to lookup paths' |
|
, ' -c, --compress Compress CSS output' |
|
, ' -d, --compare Display input along with output' |
|
, ' -f, --firebug Emits debug infos in the generated CSS that' |
|
, ' can be used by the FireStylus Firebug plugin' |
|
, ' -l, --line-numbers Emits comments in the generated CSS' |
|
, ' indicating the corresponding Stylus line' |
|
, ' -m, --sourcemap Generates a sourcemap in sourcemaps v3 format' |
|
, ' --sourcemap-inline Inlines sourcemap with full source text in base64 format' |
|
, ' --sourcemap-root <url> "sourceRoot" property of the generated sourcemap' |
|
, ' --sourcemap-base <path> Base <path> from which sourcemap and all sources are relative' |
|
, ' -P, --prefix [prefix] prefix all css classes' |
|
, ' -p, --print Print out the compiled CSS' |
|
, ' --import <file> Import stylus <file>' |
|
, ' --include-css Include regular CSS on @import' |
|
, ' -D, --deps Display dependencies of the compiled file' |
|
, ' --disable-cache Disable caching' |
|
, ' --hoist-atrules Move @import and @charset to the top' |
|
, ' -r, --resolve-url Resolve relative urls inside imports' |
|
, ' --resolve-url-nocheck Like --resolve-url but without file existence check' |
|
, ' -V, --version Display the version of Stylus' |
|
, ' -h, --help Display help information' |
|
, '' |
|
].join('\n'); |
|
|
|
/** |
|
* Handle arguments. |
|
*/ |
|
|
|
var arg; |
|
while (args.length) { |
|
arg = args.shift(); |
|
switch (arg) { |
|
case '-h': |
|
case '--help': |
|
console.error(usage); |
|
process.exit(0); |
|
case '-d': |
|
case '--compare': |
|
compare = true; |
|
break; |
|
case '-c': |
|
case '--compress': |
|
compress = true; |
|
break; |
|
case '-C': |
|
case '--css': |
|
convertCSS = true; |
|
break; |
|
case '-f': |
|
case '--firebug': |
|
firebug = true; |
|
break; |
|
case '-l': |
|
case '--line-numbers': |
|
linenos = true; |
|
break; |
|
case '-m': |
|
case '--sourcemap': |
|
sourcemap = {}; |
|
break; |
|
case '--sourcemap-inline': |
|
sourcemap = sourcemap || {}; |
|
sourcemap.inline = true; |
|
break; |
|
case '--sourcemap-root': |
|
var url = args.shift(); |
|
if (!url) throw new Error('--sourcemap-root <url> required'); |
|
sourcemap = sourcemap || {}; |
|
sourcemap.sourceRoot = url; |
|
break; |
|
case '--sourcemap-base': |
|
var path = args.shift(); |
|
if (!path) throw new Error('--sourcemap-base <path> required'); |
|
sourcemap = sourcemap || {}; |
|
sourcemap.basePath = path; |
|
break; |
|
case '-P': |
|
case '--prefix': |
|
prefix = args.shift(); |
|
if (!prefix) throw new Error('--prefix <prefix> required'); |
|
break; |
|
case '-p': |
|
case '--print': |
|
print = true; |
|
break; |
|
case '-V': |
|
case '--version': |
|
console.log(stylus.version); |
|
process.exit(0); |
|
break; |
|
case '-o': |
|
case '--out': |
|
dest = args.shift(); |
|
if (!dest) throw new Error('--out <dir> required'); |
|
break; |
|
case 'help': |
|
var name = args.shift() |
|
, browser = name.split(':'); |
|
if (browser.length > 1) { |
|
name = [].slice.call(browser, 1).join(':'); |
|
browser = browser[0]; |
|
} else { |
|
name = browser[0]; |
|
browser = ''; |
|
} |
|
if (!name) throw new Error('help <property> required'); |
|
help(name); |
|
break; |
|
case '--include-css': |
|
includeCSS = true; |
|
break; |
|
case '--disable-cache': |
|
disableCache = true; |
|
break; |
|
case '--hoist-atrules': |
|
hoist = true; |
|
break; |
|
case '-i': |
|
case '--repl': |
|
case '--interactive': |
|
interactive = true; |
|
break; |
|
case '-I': |
|
case '--include': |
|
var path = args.shift(); |
|
if (!path) throw new Error('--include <path> required'); |
|
paths.push(path); |
|
break; |
|
case '-w': |
|
case '--watch': |
|
watchers = {}; |
|
break; |
|
case '-U': |
|
case '--inline': |
|
args.unshift('--use', 'url'); |
|
break; |
|
case '-u': |
|
case '--use': |
|
var options; |
|
var path = args.shift(); |
|
if (!path) throw new Error('--use <path> required'); |
|
|
|
// options |
|
if ('--with' == args[0]) { |
|
args.shift(); |
|
options = args.shift(); |
|
if (!options) throw new Error('--with <options> required'); |
|
options = eval('(' + options + ')'); |
|
} |
|
|
|
// url support |
|
if ('url' == path) { |
|
urlFunction = options || {}; |
|
} else { |
|
paths.push(dirname(path)); |
|
plugins.push({ path: path, options: options }); |
|
} |
|
break; |
|
case '--import': |
|
var file = args.shift(); |
|
if (!file) throw new Error('--import <file> required'); |
|
imports.push(file); |
|
break; |
|
case '-r': |
|
case '--resolve-url': |
|
resolveURL = {}; |
|
break; |
|
case '--resolve-url-nocheck': |
|
resolveURL = { nocheck: true }; |
|
break; |
|
case '-D': |
|
case '--deps': |
|
deps = true; |
|
break; |
|
default: |
|
files.push(arg); |
|
} |
|
} |
|
|
|
// if --watch is used, assume we are |
|
// not working with stdio |
|
|
|
if (watchers && !files.length) { |
|
files = fs.readdirSync(process.cwd()) |
|
.filter(function(file){ |
|
return file.match(/\.styl$/); |
|
}); |
|
} |
|
|
|
// --sourcemap flag is not working with stdio |
|
if (sourcemap && !files.length) sourcemap = false; |
|
|
|
/** |
|
* Open the default browser to the CSS property `name`. |
|
* |
|
* @param {String} name |
|
*/ |
|
|
|
function help(name) { |
|
var url |
|
, exec = require('child_process').exec |
|
, command; |
|
|
|
name = encodeURIComponent(name); |
|
|
|
switch (browser) { |
|
case 'safari': |
|
case 'webkit': |
|
url = 'https://developer.apple.com/library/safari/search/?q=' + name; |
|
break; |
|
case 'opera': |
|
url = 'http://dev.opera.com/search/?term=' + name; |
|
break; |
|
case 'w3c': |
|
url = 'http://www.google.com/search?q=site%3Awww.w3.org%2FTR+' + name; |
|
break; |
|
case 'ms': |
|
url = 'http://social.msdn.microsoft.com/search/en-US/ie?query=' + name + '&refinement=59%2c61'; |
|
break; |
|
case 'caniuse': |
|
url = 'http://caniuse.com/#search=' + name; |
|
break; |
|
case 'quirksmode': |
|
url = 'http://www.google.com/search?q=site%3Awww.quirksmode.org+' + name; |
|
break; |
|
default: |
|
url = 'https://developer.mozilla.org/en/CSS/' + name; |
|
} |
|
|
|
switch (process.platform) { |
|
case 'linux': command = 'x-www-browser'; break; |
|
default: command = 'open'; |
|
} |
|
|
|
exec(command + ' "' + url + '"', function(){ |
|
process.exit(0); |
|
}); |
|
} |
|
|
|
// Compilation options |
|
|
|
if (files.length > 1 && extname(dest) === '.css') { |
|
dest = dirname(dest); |
|
} |
|
|
|
var options = { |
|
filename: 'stdin' |
|
, compress: compress |
|
, firebug: firebug |
|
, linenos: linenos |
|
, sourcemap: sourcemap |
|
, paths: [process.cwd()].concat(paths) |
|
, prefix: prefix |
|
, dest: dest |
|
, 'hoist atrules': hoist |
|
}; |
|
|
|
// Buffer stdin |
|
|
|
var str = ''; |
|
|
|
// Convert CSS to Stylus |
|
|
|
if (convertCSS) { |
|
switch (files.length) { |
|
case 2: |
|
compileCSSFile(files[0], files[1]); |
|
break; |
|
case 1: |
|
var file = files[0]; |
|
compileCSSFile(file, join(dirname(file), basename(file, extname(file))) + '.styl'); |
|
break; |
|
default: |
|
var stdin = process.openStdin(); |
|
stdin.setEncoding('utf8'); |
|
stdin.on('data', function(chunk){ str += chunk; }); |
|
stdin.on('end', function(){ |
|
var out = stylus.convertCSS(str); |
|
console.log(out); |
|
}); |
|
} |
|
} else if (interactive) { |
|
repl(); |
|
} else if (deps) { |
|
// if --deps is used, just display list of the dependencies |
|
// not working with stdio and dirs |
|
displayDeps(); |
|
} else { |
|
if (files.length) { |
|
compileFiles(files); |
|
} else { |
|
compileStdio(); |
|
} |
|
} |
|
|
|
/** |
|
* Start Stylus REPL. |
|
*/ |
|
|
|
function repl() { |
|
var options = { cache: false, filename: 'stdin', imports: [join(__dirname, '..', 'lib', 'functions')] } |
|
, parser = new stylus.Parser('', options) |
|
, evaluator = new stylus.Evaluator(parser.parse(), options) |
|
, rl = require('readline') |
|
, repl = rl.createInterface(process.stdin, process.stdout, autocomplete) |
|
, global = evaluator.global.scope; |
|
|
|
// expose BIFs |
|
evaluator.evaluate(); |
|
|
|
// readline |
|
repl.setPrompt('> '); |
|
repl.prompt(); |
|
|
|
// HACK: flat-list auto-complete |
|
function autocomplete(line){ |
|
var out = process.stdout |
|
, keys = Object.keys(global.locals) |
|
, len = keys.length |
|
, words = line.split(/\s+/) |
|
, word = words.pop() |
|
, names = [] |
|
, name |
|
, node |
|
, key; |
|
|
|
// find words that match |
|
for (var i = 0; i < len; ++i) { |
|
key = keys[i]; |
|
if (0 == key.indexOf(word)) { |
|
node = global.lookup(key); |
|
switch (node.nodeName) { |
|
case 'function': |
|
names.push(node.toString()); |
|
break; |
|
default: |
|
names.push(key); |
|
} |
|
} |
|
} |
|
|
|
return [names, line]; |
|
}; |
|
|
|
repl.on('line', function(line){ |
|
if (!line.trim().length) return repl.prompt(); |
|
parser = new stylus.Parser(line, options); |
|
parser.state.push('expression'); |
|
evaluator.return = true; |
|
try { |
|
var expr = parser.parse(); |
|
evaluator.root = expr; |
|
var ret = evaluator.evaluate(); |
|
var node; |
|
while (node = ret.nodes.pop()) { |
|
if (!node.suppress) { |
|
var str = node.toString(); |
|
if ('(' == str[0]) str = str.replace(/^\(|\)$/g, ''); |
|
console.log('\033[90m=> \033[0m' + highlight(str)); |
|
break; |
|
} |
|
} |
|
repl.prompt(); |
|
} catch (err) { |
|
console.error('\033[31merror: %s\033[0m', err.message || err.stack); |
|
repl.prompt(); |
|
} |
|
}); |
|
|
|
repl.on('SIGINT', function(){ |
|
console.log(); |
|
process.exit(0); |
|
}); |
|
} |
|
|
|
/** |
|
* Highlight the given string of Stylus. |
|
*/ |
|
|
|
function highlight(str) { |
|
return str |
|
.replace(/(#)?(\d+(\.\d+)?)/g, function($0, $1, $2){ |
|
return $1 ? $0 : '\033[36m' + $2 + '\033[0m'; |
|
}) |
|
.replace(/(#[\da-fA-F]+)/g, '\033[33m$1\033[0m') |
|
.replace(/('.*?'|".*?")/g, '\033[32m$1\033[0m'); |
|
} |
|
|
|
/** |
|
* Convert a CSS file to a Styl file |
|
*/ |
|
|
|
function compileCSSFile(file, fileOut) { |
|
fs.stat(file, function(err, stat){ |
|
if (err) throw err; |
|
if (stat.isFile()) { |
|
fs.readFile(file, 'utf8', function(err, str){ |
|
if (err) throw err; |
|
var styl = stylus.convertCSS(str); |
|
fs.writeFile(fileOut, styl, function(err){ |
|
if (err) throw err; |
|
}); |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
/** |
|
* Compile with stdio. |
|
*/ |
|
|
|
function compileStdio() { |
|
process.stdin.setEncoding('utf8'); |
|
process.stdin.on('data', function(chunk){ str += chunk; }); |
|
process.stdin.on('end', function(){ |
|
// Compile to css |
|
var style = stylus(str, options); |
|
if (includeCSS) style.set('include css', true); |
|
if (disableCache) style.set('cache', false); |
|
usePlugins(style); |
|
importFiles(style); |
|
style.render(function(err, css){ |
|
if (err) throw err; |
|
if (compare) { |
|
console.log('\n\x1b[1mInput:\x1b[0m'); |
|
console.log(str); |
|
console.log('\n\x1b[1mOutput:\x1b[0m'); |
|
} |
|
console.log(css); |
|
if (compare) console.log(); |
|
}); |
|
}).resume(); |
|
} |
|
|
|
/** |
|
* Compile the given files. |
|
*/ |
|
|
|
function compileFiles(files) { |
|
files.forEach(compileFile); |
|
} |
|
|
|
/** |
|
* Display dependencies of the compiled files. |
|
*/ |
|
|
|
function displayDeps() { |
|
files.forEach(function(file){ |
|
// ensure file exists |
|
fs.stat(file, function(err, stat){ |
|
if (err) throw err; |
|
fs.readFile(file, 'utf8', function(err, str){ |
|
if (err) throw err; |
|
options.filename = file; |
|
var style = stylus(str, options); |
|
|
|
usePlugins(style); |
|
importFiles(style); |
|
console.log(style.deps().join('\n')); |
|
}); |
|
}); |
|
}); |
|
} |
|
|
|
/** |
|
* Compile the given file. |
|
*/ |
|
|
|
function compileFile(file) { |
|
// ensure file exists |
|
fs.stat(file, function(err, stat){ |
|
if (err) throw err; |
|
// file |
|
if (stat.isFile()) { |
|
fs.readFile(file, 'utf8', function(err, str){ |
|
if (err) throw err; |
|
options.filename = file; |
|
options._imports = []; |
|
var style = stylus(str, options); |
|
if (includeCSS) style.set('include css', true); |
|
if (disableCache) style.set('cache', false); |
|
if (sourcemap) style.set('sourcemap', sourcemap); |
|
|
|
usePlugins(style); |
|
importFiles(style); |
|
style.render(function(err, css){ |
|
watchImports(file, options._imports); |
|
if (err) { |
|
if (watchers) { |
|
console.error(err.stack || err.message); |
|
} else { |
|
throw err; |
|
} |
|
} else { |
|
writeFile(file, css); |
|
// write sourcemap |
|
if (sourcemap && !sourcemap.inline) { |
|
writeSourcemap(file, style.sourcemap); |
|
} |
|
} |
|
}); |
|
}); |
|
// directory |
|
} else if (stat.isDirectory()) { |
|
fs.readdir(file, function(err, files){ |
|
if (err) throw err; |
|
files.filter(function(path){ |
|
return path.match(/\.styl$/); |
|
}).map(function(path){ |
|
return join(file, path); |
|
}).forEach(compileFile); |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
/** |
|
* Write the given CSS output. |
|
*/ |
|
|
|
function createPath(file, sourceMap) { |
|
var out; |
|
if (files.length === 1 && extname(dest) === '.css') { |
|
return [dest, sourceMap ? '.map' : ''].join(''); |
|
} |
|
// --out support |
|
out = [basename(file, extname(file)), sourceMap ? '.css.map' : '.css'].join(''); |
|
return dest |
|
? join(dest, out) |
|
: join(dirname(file), out); |
|
} |
|
|
|
function writeFile(file, css) { |
|
// --print support |
|
if (print) return process.stdout.write(css); |
|
var path = createPath(file); |
|
fs.writeFile(path, css, function(err){ |
|
if (err) throw err; |
|
console.log(' \033[90mcompiled\033[0m %s', path); |
|
// --watch support |
|
watch(file, file); |
|
}); |
|
} |
|
|
|
/** |
|
* Write the given sourcemap. |
|
*/ |
|
|
|
function writeSourcemap(file, sourcemap) { |
|
var path = createPath(file, true); |
|
fs.writeFile(path, JSON.stringify(sourcemap), function(err){ |
|
if (err) throw err; |
|
// don't output log message if --print is present |
|
if (!print) console.log(' \033[90mgenerated\033[0m %s', path); |
|
}); |
|
} |
|
|
|
/** |
|
* Watch the given `file` and recompiling `rootFile` when modified. |
|
*/ |
|
|
|
function watch(file, rootFile) { |
|
// not watching |
|
if (!watchers) return; |
|
|
|
// already watched |
|
if (watchers[file]) { |
|
watchers[file][rootFile] = true; |
|
return; |
|
} |
|
|
|
// watch the file itself |
|
watchers[file] = {}; |
|
watchers[file][rootFile] = true; |
|
if (print) { |
|
console.error('Stylus CLI Error: Watch and print cannot be used together'); |
|
process.exit(1); |
|
} |
|
console.log(' \033[90mwatching\033[0m %s', file); |
|
// if is windows use fs.watch api instead |
|
// TODO: remove watchFile when fs.watch() works on osx etc |
|
if (isWindows) { |
|
fs.watch(file, function(event) { |
|
if (event === 'change') compile(); |
|
}); |
|
} else { |
|
fs.watchFile(file, { interval: 300 }, function(curr, prev) { |
|
if (curr.mtime > prev.mtime) compile(); |
|
}); |
|
} |
|
|
|
function compile() { |
|
for (var rootFile in watchers[file]) { |
|
compileFile(rootFile); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Watch `imports`, re-compiling `file` when they change. |
|
*/ |
|
|
|
function watchImports(file, imports) { |
|
imports.forEach(function(imported){ |
|
if (!imported.path) return; |
|
watch(imported.path, file); |
|
}); |
|
} |
|
|
|
/** |
|
* Utilize plugins. |
|
*/ |
|
|
|
function usePlugins(style) { |
|
plugins.forEach(function(plugin){ |
|
var path = plugin.path; |
|
var options = plugin.options; |
|
fn = require(/^\.+\//.test(path) ? resolve(path) : path); |
|
if ('function' != typeof fn) { |
|
throw new Error('plugin ' + path + ' does not export a function'); |
|
} |
|
style.use(fn(options)); |
|
}); |
|
|
|
if (urlFunction) { |
|
style.define('url', stylus.url(urlFunction)); |
|
} else if (resolveURL) { |
|
style.define('url', stylus.resolver(resolveURL)); |
|
} |
|
} |
|
|
|
/** |
|
* Imports the indicated files. |
|
*/ |
|
|
|
function importFiles(style) { |
|
imports.forEach(function(file) { |
|
style.import(file); |
|
}); |
|
}
|
|
|