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.

247 lines
5.4 KiB

/*!
* Stylus - Renderer
* Copyright (c) Automattic <developer.wordpress.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var Parser = require('./parser')
, EventEmitter = require('events').EventEmitter
, Evaluator = require('./visitor/evaluator')
, Normalizer = require('./visitor/normalizer')
, events = new EventEmitter
, utils = require('./utils')
, nodes = require('./nodes')
, join = require('path').join;
/**
* Expose `Renderer`.
*/
module.exports = Renderer;
/**
* Initialize a new `Renderer` with the given `str` and `options`.
*
* @param {String} str
* @param {Object} options
* @api public
*/
function Renderer(str, options) {
options = options || {};
options.globals = options.globals || {};
options.functions = options.functions || {};
options.use = options.use || [];
options.use = Array.isArray(options.use) ? options.use : [options.use];
options.imports = [join(__dirname, 'functions')];
options.paths = options.paths || [];
options.filename = options.filename || 'stylus';
options.Evaluator = options.Evaluator || Evaluator;
this.options = options;
this.str = str;
this.events = events;
};
/**
* Inherit from `EventEmitter.prototype`.
*/
Renderer.prototype.__proto__ = EventEmitter.prototype;
/**
* Expose events explicitly.
*/
module.exports.events = events;
/**
* Parse and evaluate AST, then callback `fn(err, css, js)`.
*
* @param {Function} fn
* @api public
*/
Renderer.prototype.render = function(fn){
var parser = this.parser = new Parser(this.str, this.options);
// use plugin(s)
for (var i = 0, len = this.options.use.length; i < len; i++) {
this.use(this.options.use[i]);
}
try {
nodes.filename = this.options.filename;
// parse
var ast = parser.parse();
// evaluate
this.evaluator = new this.options.Evaluator(ast, this.options);
this.nodes = nodes;
this.evaluator.renderer = this;
ast = this.evaluator.evaluate();
// normalize
var normalizer = new Normalizer(ast, this.options);
ast = normalizer.normalize();
// compile
var compiler = this.options.sourcemap
? new (require('./visitor/sourcemapper'))(ast, this.options)
: new (require('./visitor/compiler'))(ast, this.options)
, css = compiler.compile();
// expose sourcemap
if (this.options.sourcemap) this.sourcemap = compiler.map.toJSON();
} catch (err) {
var options = {};
options.input = err.input || this.str;
options.filename = err.filename || this.options.filename;
options.lineno = err.lineno || parser.lexer.lineno;
options.column = err.column || parser.lexer.column;
if (!fn) throw utils.formatException(err, options);
return fn(utils.formatException(err, options));
}
// fire `end` event
var listeners = this.listeners('end');
if (fn) listeners.push(fn);
for (var i = 0, len = listeners.length; i < len; i++) {
var ret = listeners[i](null, css);
if (ret) css = ret;
}
if (!fn) return css;
};
/**
* Get dependencies of the compiled file.
*
* @param {String} [filename]
* @return {Array}
* @api public
*/
Renderer.prototype.deps = function(filename){
var opts = utils.merge({ cache: false }, this.options);
if (filename) opts.filename = filename;
var DepsResolver = require('./visitor/deps-resolver')
, parser = new Parser(this.str, opts);
try {
nodes.filename = opts.filename;
// parse
var ast = parser.parse()
, resolver = new DepsResolver(ast, opts);
// resolve dependencies
return resolver.resolve();
} catch (err) {
var options = {};
options.input = err.input || this.str;
options.filename = err.filename || opts.filename;
options.lineno = err.lineno || parser.lexer.lineno;
options.column = err.column || parser.lexer.column;
throw utils.formatException(err, options);
}
};
/**
* Set option `key` to `val`.
*
* @param {String} key
* @param {Mixed} val
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.set = function(key, val){
this.options[key] = val;
return this;
};
/**
* Get option `key`.
*
* @param {String} key
* @return {Mixed} val
* @api public
*/
Renderer.prototype.get = function(key){
return this.options[key];
};
/**
* Include the given `path` to the lookup paths array.
*
* @param {String} path
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.include = function(path){
this.options.paths.push(path);
return this;
};
/**
* Use the given `fn`.
*
* This allows for plugins to alter the renderer in
* any way they wish, exposing paths etc.
*
* @param {Function}
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.use = function(fn){
fn.call(this, this);
return this;
};
/**
* Define function or global var with the given `name`. Optionally
* the function may accept full expressions, by setting `raw`
* to `true`.
*
* @param {String} name
* @param {Function|Node} fn
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.define = function(name, fn, raw){
fn = utils.coerce(fn, raw);
if (fn.nodeName) {
this.options.globals[name] = fn;
return this;
}
// function
this.options.functions[name] = fn;
if (undefined != raw) fn.raw = raw;
return this;
};
/**
* Import the given `file`.
*
* @param {String} file
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.import = function(file){
this.options.imports.push(file);
return this;
};