UNPKG

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
// 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);