cxltx
Version:
CXLTX (CoffeeXeLaTeX) brings CoffeeScript to (Xe)(La)TeX documents. Fully general approach; use your own favorite language.
297 lines (245 loc) • 9.42 kB
JavaScript
// Generated by CoffeeScript 1.8.0
(function() {
var Line_by_line, TRM, TYPES, alert, badge, debug, echo, eventually, help, info, log, njs_fs, njs_path, rpr, warn, whisper, _echo,
__slice = [].slice;
njs_fs = require('fs');
njs_path = require('path');
TYPES = require('coffeenode-types');
TRM = require('coffeenode-trm');
rpr = TRM.rpr.bind(TRM);
badge = 'CXLTX/main';
log = TRM.get_logger('plain', badge);
info = TRM.get_logger('info', badge);
whisper = TRM.get_logger('whisper', badge);
alert = TRM.get_logger('alert', badge);
warn = TRM.get_logger('warn', badge);
help = TRM.get_logger('help', badge);
_echo = TRM.echo.bind(TRM);
eventually = process.nextTick;
Line_by_line = require('line-by-line');
this.dispatch = function(provider, texroute, splitter, command, parameter, handler) {
var arity, idx, jobname, method, method_name, parameters, _i, _ref;
jobname = njs_path.basename(texroute);
texroute = njs_path.dirname(texroute);
method_name = command.replace(/-/g, '_');
method = provider[method_name];
arity = method.length;
if (method == null) {
return handler("unknown method " + (rpr(method_name)));
}
if (parameter != null) {
parameter = this.decode_literal_utf8(parameter);
parameters = parameter.length === 0 ? [] : parameter.split(splitter);
} else {
parameter = '';
parameters = [];
}
for (idx = _i = 0, _ref = arity - 1 - parameters.length; _i < _ref; idx = _i += 1) {
parameters.push(void 0);
}
this.aux['is-complete'] = false;
this.aux['texroute'] = texroute;
this.aux['auxroute'] = (njs_path.join(texroute, jobname)).concat('.auxcopy');
this.aux['jobname'] = jobname;
this.aux['splitter'] = splitter;
this.aux['method-name'] = method_name;
this.aux['parameters'] = parameters;
method.call.apply(method, [provider].concat(__slice.call(parameters), [handler]));
return null;
};
this.aux = {};
this.read_aux = function(handler) {
var auxroute, duplicate_labels, jobname, labels, postprocess, texroute;
if (this.aux['is-complete']) {
return handler(null, this.aux);
}
texroute = this.aux['texroute'];
jobname = this.aux['jobname'];
auxroute = this.aux['auxroute'];
debug('©fdZOh', this.aux);
if (!njs_fs.existsSync(auxroute)) {
warn("unable to locate " + auxroute + "; ignoring");
eventually((function(_this) {
return function() {
return handler(null);
};
})(this));
return null;
}
this.aux['auxroute'] = auxroute;
this.aux['labels'] = labels = {};
this.aux['duplicate-labels'] = duplicate_labels = {};
this._lines_of(auxroute, (function(_this) {
return function(error, line, line_nr) {
var coffee, duplicate, ignore, is_duplicate, label, match, name, pageref, ref, source, title, type, unknown, value, x;
if (error != null) {
return handler(error);
}
if (line === null) {
postprocess();
_this.aux['is-complete'] = true;
return handler(null, _this.aux);
}
/* De-escaping characters: */
line = line.replace(_this.read_aux.protectchar_matcher, function($0, $1) {
return String.fromCharCode(parseInt($1, 16));
});
/* Compiling and evaluating CoffeeScript: */
coffee = null;
if ((match = line.match(_this.read_aux.coffeescript_matcher)) != null) {
if (coffee == null) {
coffee = require('coffee-script');
}
try {
source = coffee.compile(match[1], {
'bare': true,
'filename': auxroute
});
x = eval(source);
} catch (_error) {
error = _error;
warn("unable to parse line " + line_nr + " of " + auxroute + ":");
warn(line);
warn(rpr(error));
return null;
}
switch (type = TYPES.type_of(x)) {
case 'pod':
for (name in x) {
value = x[name];
_this.aux[name] = value;
}
break;
default:
warn("ignoring value of type " + type + " on line " + line_nr + " of " + auxroute + ":\n" + (rpr(line)));
}
return null;
}
/* Parsing labels and references: */
if ((match = line.match(_this.read_aux.newlabel_matcher)) != null) {
ignore = match[0], name = match[1], ref = match[2], pageref = match[3], title = match[4], unknown = match[5], unknown = match[6];
is_duplicate = false;
if ((duplicate = labels[name]) != null) {
is_duplicate = true;
duplicate['is-duplicate'] = true;
if (duplicate_labels[name] == null) {
duplicate_labels[name] = [duplicate];
}
}
label = {
'name': name,
'ref': ref,
'pageref': pageref,
'title': title,
'is-duplicate': is_duplicate
};
labels[name] = label;
if (is_duplicate) {
duplicate_labels[name].push(label);
}
return null;
}
};
})(this));
postprocess = (function(_this) {
return function() {
/* Postprocessing of the data delivered by the `\auxgeo` command.
All resulting lemgths are in millimeters. `firstlinev` is the distance between the
top of the paper and the top of the first line of text. Similarly, the implicit 1 inch distance in
`\voffset` and `\hoffset` is being made explicit so that the reference point is shifted to the paper's
top left corner.
See http://www.ctex.org/documents/packages/layout/layman.pdf p9 and
http://en.wikibooks.org/wiki/LaTeX/Page_Layout
*/
var g, name, one_inch, value;
one_inch = 4736286;
if ((g = _this.aux['geometry']) != null) {
for (name in g) {
value = g[name];
if (name === 'voffset') {
value += one_inch;
}
if (name === 'hoffset') {
value += one_inch;
}
g[name] = (Math.round(value / 39158276 * 210 * 10000 + 0.5)) / 10000;
}
return g['firstlinev'] = g['voffset'] + g['topmargin'] + g['headsep'] + g['headheight'];
}
};
})(this);
return null;
};
/* matcher for those uber-verbosely: `\protect \char "007B\relax` escaped characters: */
this.read_aux.protectchar_matcher = /\\protect\s+\\char\s+"([0-9A-F]+)\\relax\s?/g;
/* matcher for CoffeeScript: */
this.read_aux.coffeescript_matcher = /^%\s+coffee\s+(.+)$/;
/* \newlabel{otherlabel}{{2}{3}} */
/* \newlabel{otherlabel}{{2}{3}{References}{section.2}{}} */
/* TAINT not sure whether this RegEx is backtracking-safe as per
http://www.regular-expressions.info/catastrophic.html
*/
this.read_aux.newlabel_matcher = /^\\newlabel\{([^{}]+)\}\{\{([0-9.]*)\}\{([0-9]*)\}(?:\{([^{}]*)\}\{([^{}]*)\}\{([^{}]*)\})?\}$/;
this._lines_of = function(route, handler) {
var line_nr, line_reader;
line_nr = 0;
line_reader = new Line_by_line(route);
line_reader.on('error', (function(_this) {
return function(error) {
return handler(error);
};
})(this));
line_reader.on('end', (function(_this) {
return function() {
return handler(null, null);
};
})(this));
line_reader.on('line', (function(_this) {
return function(line) {
line_nr += 1;
return handler(null, line, line_nr);
};
})(this));
return null;
};
this.debug = function(message) {
if (message == null) {
return echo();
}
warn(message);
return this._pen_debug(message);
};
this._pen_debug = function(message) {
message = this.escape_error(message);
return "\\textbf{\\textcolor{red}{" + message + "}}";
};
this.echo = function() {
var P;
P = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
return _echo.apply(null, P);
};
debug = this.debug.bind(this);
echo = this.echo.bind(this);
this._escape_replacements = [[/\\/g, '\\textbackslash{}'], [/\{/g, '\\{'], [/\}/g, '\\}'], [/&/g, '\\&'], [/\$/g, '\\$'], [/\#/g, '\\#'], [/%/g, '\\%'], [/_/g, '\\_'], [/\^/g, '\\textasciicircum{}'], [/~/g, '\\textasciitilde{}']];
this.escape = function(text) {
var R, matcher, replacement, _i, _len, _ref, _ref1;
R = text;
_ref = this._escape_replacements;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
_ref1 = _ref[_i], matcher = _ref1[0], replacement = _ref1[1];
R = R.replace(matcher, replacement);
}
return R;
};
this.escape_error = function(text) {
/* Escape all characters that would cause difficulties with TeX's `\verbatiminput`. */
return (this.escape(text)).replace(/\n+/g, '\\par\n\n');
};
this.decode_literal_utf8 = function(text) {
/* The `Buffer ... toString` steps decode literal UTF-8 in the request */
/* TAINT the NodeJS docs say: [the 'binary'] encoding method is deprecated and should be avoided
[...] [it] will be removed in future versions of Node
*/
return (new Buffer(text, 'binary')).toString('utf-8');
};
}).call(this);