"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.splitCommand = exports.getCommand = exports.walkTemplate = exports.findHighestImgId = exports.produceJsReport = exports.extractQuery = exports.newContext = void 0;
var reportUtils_1 = require("./reportUtils");
var jsSandbox_1 = require("./jsSandbox");
var types_1 = require("./types");
var errors_1 = require("./errors");
var debug_1 = require("./debug");
function newContext(options, imageAndShapeIdIncrement) {
if (imageAndShapeIdIncrement === void 0) { imageAndShapeIdIncrement = 0; }
return {
gCntIf: 0,
gCntEndIf: 0,
level: 1,
fCmd: false,
cmd: '',
fSeekQuery: false,
buffers: {
'w:p': { text: '', cmds: '', fInsertedText: false },
'w:tr': { text: '', cmds: '', fInsertedText: false },
'w:tc': { text: '', cmds: '', fInsertedText: false },
},
imageAndShapeIdIncrement: imageAndShapeIdIncrement,
images: {},
linkId: 0,
links: {},
htmlId: 0,
htmls: {},
vars: {},
loops: [],
fJump: false,
shorthands: {},
options: options,
// To verfiy we don't have a nested if within the same p or tr tag
pIfCheckMap: new Map(),
trIfCheckMap: new Map(),
};
}
exports.newContext = newContext;
// Go through the document until the query string is found (normally at the beginning)
function extractQuery(template, options) {
return __awaiter(this, void 0, void 0, function () {
var ctx, nodeIn, fFound, parent_1, nextSibling, parent_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
ctx = newContext(options);
// ensure no command will be processed, except QUERY
ctx.fSeekQuery = true;
nodeIn = template;
_a.label = 1;
case 1:
if (!true) return [3 /*break*/, 4];
// Move down
if (nodeIn._children.length)
nodeIn = nodeIn._children[0];
else {
fFound = false;
while (nodeIn._parent != null) {
parent_1 = nodeIn._parent;
nextSibling = (0, reportUtils_1.getNextSibling)(nodeIn);
if (nextSibling) {
nodeIn = nextSibling;
fFound = true;
break;
}
nodeIn = parent_1;
}
if (!fFound)
return [3 /*break*/, 4];
}
if (!nodeIn)
return [3 /*break*/, 4];
parent_2 = nodeIn._parent;
if (!(nodeIn._fTextNode &&
parent_2 &&
!parent_2._fTextNode &&
parent_2._tag === 'w:t')) return [3 /*break*/, 3];
return [4 /*yield*/, processText(null, nodeIn, ctx, processCmd)];
case 2:
_a.sent();
_a.label = 3;
case 3:
if (ctx.query != null)
return [3 /*break*/, 4];
return [3 /*break*/, 1];
case 4: return [2 /*return*/, ctx.query];
}
});
});
}
exports.extractQuery = extractQuery;
function produceJsReport(data, template, ctx) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, walkTemplate(data, template, ctx, processCmd)];
});
});
}
exports.produceJsReport = produceJsReport;
function findHighestImgId(mainDoc) {
var doc_ids = [];
var search = function (n) {
for (var _i = 0, _a = n._children; _i < _a.length; _i++) {
var c = _a[_i];
var tag = c._fTextNode ? null : c._tag;
if (tag == null)
continue;
if (tag === 'wp:docPr') {
if (c._fTextNode)
continue;
var raw = c._attrs.id;
if (typeof raw !== 'string')
continue;
var id = Number.parseInt(raw, 10);
if (Number.isSafeInteger(id))
doc_ids.push(id);
}
if (c._children.length > 0)
search(c);
}
};
search(mainDoc);
if (doc_ids.length > 0)
return Math.max.apply(Math, doc_ids);
return 0;
}
exports.findHighestImgId = findHighestImgId;
var debugPrintNode = function (node) {
return JSON.stringify(node._fTextNode
? {
_ifName: node._ifName,
_fTextNode: node._fTextNode,
_text: node === null || node === void 0 ? void 0 : node._text,
}
: {
_ifName: node._ifName,
_fTextNode: node._fTextNode,
_tag: node === null || node === void 0 ? void 0 : node._tag,
_attrs: node === null || node === void 0 ? void 0 : node._attrs,
});
};
var findParentPorTrNode = function (node) {
var parentNode = node._parent;
var resultNode = null;
while (parentNode != null && resultNode == null) {
var parentNodeTag = parentNode._fTextNode ? null : parentNode._tag;
if (parentNodeTag === 'w:p') {
// check also for w:tr tag
var grandParentNode = parentNode._parent != null ? parentNode._parent._parent : null;
if (grandParentNode != null &&
!grandParentNode._fTextNode &&
grandParentNode._tag === 'w:tr') {
resultNode = grandParentNode;
}
else {
resultNode = parentNode;
}
}
parentNode = parentNode._parent;
}
return resultNode;
};
function walkTemplate(data, template, ctx, processor) {
return __awaiter(this, void 0, void 0, function () {
var out, nodeIn, nodeOut, move, deltaJump, errors, loopCount, maximumWalkingDepth, curLoop, nextSibling, parent_3, tag, fRemoveNode, buffers, nodeOutParent, imgNode, captionNodes, parent_4, linkNode, parent_5, htmlNode, parent_6, tag, newNode, newNodeTag, parent_7, result, err, innermost_loop, err;
var _a;
var _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
out = (0, reportUtils_1.cloneNodeWithoutChildren)(template);
nodeIn = template;
nodeOut = out;
deltaJump = 0;
errors = [];
loopCount = 0;
maximumWalkingDepth = ((_b = ctx.options) === null || _b === void 0 ? void 0 : _b.maximumWalkingDepth) || 1000000;
_c.label = 1;
case 1:
if (!true) return [3 /*break*/, 5];
curLoop = (0, reportUtils_1.getCurLoop)(ctx);
nextSibling = null;
// =============================================
// Move input node pointer
// =============================================
if (ctx.fJump) {
if (!curLoop)
throw new errors_1.InternalError('jumping while curLoop is null');
// TODO: comment debug statements back out, as creating the debug string creates overhead.
debug_1.logger.debug("Jumping to level ".concat(curLoop.refNodeLevel, "..."), debugPrintNode(curLoop.refNode));
deltaJump = ctx.level - curLoop.refNodeLevel;
nodeIn = curLoop.refNode;
ctx.level = curLoop.refNodeLevel;
ctx.fJump = false;
move = 'JUMP';
// Down (only if he haven't just moved up)
}
else if (nodeIn._children.length && move !== 'UP') {
nodeIn = nodeIn._children[0];
ctx.level += 1;
move = 'DOWN';
// Sideways
}
else if ((nextSibling = (0, reportUtils_1.getNextSibling)(nodeIn))) {
nodeIn = nextSibling;
move = 'SIDE';
// Up
}
else {
parent_3 = nodeIn._parent;
if (parent_3 == null) {
debug_1.logger.debug("=== parent is null, breaking after ".concat(loopCount, " loops..."));
return [3 /*break*/, 5];
}
else if (loopCount > maximumWalkingDepth) {
// adding a emergency exit to avoid infit loops
debug_1.logger.debug("=== parent is still not null after ".concat(loopCount, " loops, something must be wrong ..."), debugPrintNode(parent_3));
throw new errors_1.InternalError('infinite loop or massive dataset detected. Please review and try again');
}
nodeIn = parent_3;
ctx.level -= 1;
move = 'UP';
}
debug_1.logger.debug("Next node [".concat(move, ", level ").concat(ctx.level, "]"), debugPrintNode(nodeIn));
// =============================================
// Process input node
// =============================================
// Delete the last generated output node in several special cases
// --------------------------------------------------------------
if (move !== 'DOWN') {
tag = nodeOut._fTextNode ? null : nodeOut._tag;
fRemoveNode = false;
// Delete last generated output node if we're skipping nodes due to an empty FOR loop
if ((tag === 'w:p' ||
tag === 'w:tbl' ||
tag === 'w:tr' ||
tag === 'w:tc') &&
(0, reportUtils_1.isLoopExploring)(ctx)) {
fRemoveNode = true;
// Delete last generated output node if the user inserted a paragraph
// (or table row) with just a command
}
else if (tag === 'w:p' || tag === 'w:tr' || tag === 'w:tc') {
buffers = ctx.buffers[tag];
fRemoveNode =
buffers.text === '' && buffers.cmds !== '' && !buffers.fInsertedText;
// If the last generated output node is a table row, and it is set to be deleted,
// don't delete if it has exactly one nested row (i.e. within nested table)
if (tag === 'w:tr' && fRemoveNode) {
fRemoveNode =
nodeIn._children.filter(function (child) { return !child._fTextNode && child._tag === 'w:tr'; }).length !== 1;
}
// If the last generated output node is a table column, and it is set to be deleted,
// don't delete if it has a table as a child
if (tag === 'w:tc' && fRemoveNode) {
fRemoveNode = !(nodeOut._children.filter(function (child) { return !child._fTextNode && child._tag === 'w:tbl'; }).length > 0);
}
}
// Execute removal, if needed. The node will no longer be part of the output, but
// the parent will be accessible from the child (so that we can still move up the tree)
if (fRemoveNode && nodeOut._parent != null) {
nodeOut._parent._children.pop();
}
}
// Handle an UP movement
// ---------------------
if (move === 'UP') {
// Loop exploring? Update the reference node for the current loop
if ((0, reportUtils_1.isLoopExploring)(ctx) &&
curLoop &&
nodeIn === curLoop.refNode._parent) {
curLoop.refNode = nodeIn;
curLoop.refNodeLevel -= 1;
debug_1.logger.debug("Updated loop '".concat(curLoop.varName, "' refNode: ") + debugPrintNode(nodeIn));
}
nodeOutParent = nodeOut._parent;
if (nodeOutParent == null)
throw new errors_1.InternalError('node parent is null');
// Execute the move in the output tree
nodeOut = nodeOutParent;
// If an image was generated, replace the parent `w:t` node with
// the image node
if (ctx.pendingImageNode &&
!nodeOut._fTextNode &&
nodeOut._tag === 'w:t') {
imgNode = ctx.pendingImageNode.image;
captionNodes = ctx.pendingImageNode.caption;
parent_4 = nodeOut._parent;
if (parent_4) {
imgNode._parent = parent_4;
parent_4._children.pop();
parent_4._children.push(imgNode);
if (captionNodes) {
(_a = parent_4._children).push.apply(_a, captionNodes);
}
// Prevent containing paragraph or table row from being removed
ctx.buffers['w:p'].fInsertedText = true;
ctx.buffers['w:tr'].fInsertedText = true;
ctx.buffers['w:tc'].fInsertedText = true;
}
delete ctx.pendingImageNode;
}
// If a link was generated, replace the parent `w:r` node with
// the link node
if (ctx.pendingLinkNode &&
!nodeOut._fTextNode &&
nodeOut._tag === 'w:r') {
linkNode = ctx.pendingLinkNode;
parent_5 = nodeOut._parent;
if (parent_5) {
linkNode._parent = parent_5;
parent_5._children.pop();
parent_5._children.push(linkNode);
// Prevent containing paragraph or table row from being removed
ctx.buffers['w:p'].fInsertedText = true;
ctx.buffers['w:tr'].fInsertedText = true;
ctx.buffers['w:tc'].fInsertedText = true;
}
delete ctx.pendingLinkNode;
}
// If a html page was generated, replace the parent `w:p` node with
// the html node
if (ctx.pendingHtmlNode &&
!nodeOut._fTextNode &&
nodeOut._tag === 'w:p') {
htmlNode = ctx.pendingHtmlNode;
parent_6 = nodeOut._parent;
if (parent_6) {
htmlNode._parent = parent_6;
parent_6._children.pop();
parent_6._children.push(htmlNode);
// Prevent containing paragraph or table row from being removed
ctx.buffers['w:p'].fInsertedText = true;
ctx.buffers['w:tr'].fInsertedText = true;
ctx.buffers['w:tc'].fInsertedText = true;
}
delete ctx.pendingHtmlNode;
}
// `w:tc` nodes shouldn't be left with no `w:p` or 'w:altChunk' children; if that's the
// case, add an empty `w:p` inside
if (!nodeOut._fTextNode &&
nodeOut._tag === 'w:tc' &&
!nodeOut._children.filter(function (o) { return !o._fTextNode && (o._tag === 'w:p' || o._tag === 'w:altChunk'); }).length) {
nodeOut._children.push({
_parent: nodeOut,
_children: [],
_fTextNode: false,
_tag: 'w:p',
_attrs: {},
});
}
// Save latest `w:rPr` node that was visited (for LINK properties)
if (!nodeOut._fTextNode && nodeOut._tag === 'w:rPr') {
ctx.textRunPropsNode = nodeOut;
}
if (!nodeIn._fTextNode && nodeIn._tag === 'w:r') {
delete ctx.textRunPropsNode;
}
}
if (!(move === 'DOWN' || move === 'SIDE')) return [3 /*break*/, 4];
// Move nodeOut to point to the new node's parent
if (move === 'SIDE') {
if (nodeOut._parent == null)
throw new errors_1.InternalError('node parent is null');
nodeOut = nodeOut._parent;
}
tag = nodeIn._fTextNode ? null : nodeIn._tag;
if (tag === 'w:p' || tag === 'w:tr' || tag === 'w:tc') {
ctx.buffers[tag] = { text: '', cmds: '', fInsertedText: false };
}
newNode = (0, reportUtils_1.cloneNodeWithoutChildren)(nodeIn);
newNode._parent = nodeOut;
nodeOut._children.push(newNode);
newNodeTag = newNode._tag;
if (!(0, reportUtils_1.isLoopExploring)(ctx) &&
(newNodeTag === 'wp:docPr' || newNodeTag === 'v:shape')) {
debug_1.logger.debug('detected a - ', debugPrintNode(newNode));
updateID(newNode, ctx);
}
parent_7 = nodeIn._parent;
if (!(nodeIn._fTextNode &&
parent_7 &&
!parent_7._fTextNode &&
parent_7._tag === 'w:t')) return [3 /*break*/, 3];
return [4 /*yield*/, processText(data, nodeIn, ctx, processor)];
case 2:
result = _c.sent();
if (typeof result === 'string') {
// TODO: improve typesafety of conversion Node to TextNode.
newNode._text = result;
debug_1.logger.debug("Inserted command result string into node. Updated node: " +
debugPrintNode(newNode));
}
else {
errors.push.apply(errors, result);
}
_c.label = 3;
case 3:
// Execute the move in the output tree
nodeOut = newNode;
_c.label = 4;
case 4:
// JUMP to the target level of the tree.
// -------------------------------------------
if (move === 'JUMP') {
while (deltaJump > 0) {
if (nodeOut._parent == null)
throw new errors_1.InternalError('node parent is null');
nodeOut = nodeOut._parent;
deltaJump -= 1;
}
}
loopCount++;
return [3 /*break*/, 1];
case 5:
if (ctx.gCntIf !== ctx.gCntEndIf) {
err = new errors_1.IncompleteConditionalStatementError();
if (ctx.options.failFast) {
throw err;
}
else {
errors.push(err);
}
}
if (ctx.loops.filter(function (l) { return !l.isIf; }).length > 0) {
innermost_loop = ctx.loops[ctx.loops.length - 1];
err = new errors_1.UnterminatedForLoopError(innermost_loop);
if (ctx.options.failFast) {
throw err;
}
else {
errors.push(err);
}
}
if (errors.length > 0)
return [2 /*return*/, {
status: 'errors',
errors: errors,
}];
return [2 /*return*/, {
status: 'success',
report: out,
images: ctx.images,
links: ctx.links,
htmls: ctx.htmls,
}];
}
});
});
}
exports.walkTemplate = walkTemplate;
var processText = function (data, node, ctx, onCommand) { return __awaiter(void 0, void 0, void 0, function () {
var _a, cmdDelimiter, failFast, text, segments, outText, errors, idx, segment, cmdResultText;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = ctx.options, cmdDelimiter = _a.cmdDelimiter, failFast = _a.failFast;
text = node._text;
if (text == null || text === '')
return [2 /*return*/, ''];
segments = text
.split(cmdDelimiter[0])
.map(function (s) { return s.split(cmdDelimiter[1]); })
.reduce(function (x, y) { return x.concat(y); });
outText = '';
errors = [];
idx = 0;
_b.label = 1;
case 1:
if (!(idx < segments.length)) return [3 /*break*/, 5];
// Include the separators in the `buffers` field (used for deleting paragraphs if appropriate)
if (idx > 0)
appendTextToTagBuffers(cmdDelimiter[0], ctx, { fCmd: true });
segment = segments[idx];
// logger.debug(`Token: '${segment}' (${ctx.fCmd})`);
if (ctx.fCmd)
ctx.cmd += segment;
else if (!(0, reportUtils_1.isLoopExploring)(ctx))
outText += segment;
appendTextToTagBuffers(segment, ctx, { fCmd: ctx.fCmd });
if (!(idx < segments.length - 1)) return [3 /*break*/, 4];
if (!ctx.fCmd) return [3 /*break*/, 3];
return [4 /*yield*/, onCommand(data, node, ctx)];
case 2:
cmdResultText = _b.sent();
if (cmdResultText != null) {
if (typeof cmdResultText === 'string') {
outText += cmdResultText;
appendTextToTagBuffers(cmdResultText, ctx, {
fCmd: false,
fInsertedText: true,
});
}
else {
if (failFast)
throw cmdResultText;
errors.push(cmdResultText);
}
}
_b.label = 3;
case 3:
ctx.fCmd = !ctx.fCmd;
_b.label = 4;
case 4:
idx++;
return [3 /*break*/, 1];
case 5:
if (errors.length > 0)
return [2 /*return*/, errors];
return [2 /*return*/, outText];
}
});
}); };
// ==========================================
// Command processor
// ==========================================
var processCmd = function (data, node, ctx) { return __awaiter(void 0, void 0, void 0, function () {
var cmd, _a, cmdName, cmdRest, aliasMatch, aliasName, fullCmd, result, nerr, str, literalXmlDelimiter, splitByLineBreak, LINE_BREAK, END_OF_TEXT, START_OF_TEXT, img, pars, html, err_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
cmd = getCommand(ctx.cmd, ctx.shorthands, ctx.options.fixSmartQuotes);
ctx.cmd = ''; // flush the context
_b.label = 1;
case 1:
_b.trys.push([1, 28, , 29]);
_a = splitCommand(cmd), cmdName = _a.cmdName, cmdRest = _a.cmdRest;
if (cmdName !== 'CMD_NODE')
debug_1.logger.debug("Processing cmd: ".concat(cmd));
// Seeking query?
if (ctx.fSeekQuery) {
if (cmdName === 'QUERY')
ctx.query = cmdRest;
return [2 /*return*/];
}
if (!(cmdName === 'QUERY' || cmdName === 'CMD_NODE')) return [3 /*break*/, 2];
return [3 /*break*/, 27];
case 2:
if (!(cmdName === 'ALIAS')) return [3 /*break*/, 3];
aliasMatch = /^(\S+)\s+(.+)/.exec(cmdRest);
if (!aliasMatch)
throw new errors_1.InvalidCommandError('Invalid ALIAS command', cmd);
aliasName = aliasMatch[1];
fullCmd = aliasMatch[2];
ctx.shorthands[aliasName] = fullCmd;
debug_1.logger.debug("Defined alias '".concat(aliasName, "' for: ").concat(fullCmd));
return [3 /*break*/, 27];
case 3:
if (!(cmdName === 'FOR' || cmdName === 'IF')) return [3 /*break*/, 5];
return [4 /*yield*/, processForIf(data, node, ctx, cmd, cmdName, cmdRest)];
case 4:
_b.sent();
return [3 /*break*/, 27];
case 5:
if (!(cmdName === 'END-FOR' || cmdName === 'END-IF')) return [3 /*break*/, 6];
processEndForIf(node, ctx, cmd, cmdName, cmdRest);
return [3 /*break*/, 27];
case 6:
if (!(cmdName === 'INS')) return [3 /*break*/, 12];
if (!!(0, reportUtils_1.isLoopExploring)(ctx)) return [3 /*break*/, 11];
return [4 /*yield*/, (0, jsSandbox_1.runUserJsAndGetRaw)(data, cmdRest, ctx)];
case 7:
result = _b.sent();
if (result == null) {
return [2 /*return*/, ''];
}
if (!(typeof result === 'object' && !Array.isArray(result))) return [3 /*break*/, 10];
nerr = new errors_1.ObjectCommandResultError(cmdRest, result);
if (!(ctx.options.errorHandler != null)) return [3 /*break*/, 9];
return [4 /*yield*/, ctx.options.errorHandler(nerr, cmdRest)];
case 8:
result = _b.sent();
return [3 /*break*/, 10];
case 9: throw nerr;
case 10:
str = String(result);
if (ctx.options.processLineBreaks) {
literalXmlDelimiter = ctx.options.literalXmlDelimiter;
if (ctx.options.processLineBreaksAsNewText) {
splitByLineBreak = str.split('\n');
LINE_BREAK = "".concat(literalXmlDelimiter, "").concat(literalXmlDelimiter);
END_OF_TEXT = "".concat(literalXmlDelimiter, "").concat(literalXmlDelimiter);
START_OF_TEXT = "".concat(literalXmlDelimiter, "").concat(literalXmlDelimiter);
str = splitByLineBreak.join("".concat(END_OF_TEXT).concat(LINE_BREAK).concat(START_OF_TEXT));
}
else {
str = str.replace(/\n/g, "".concat(literalXmlDelimiter, "").concat(literalXmlDelimiter));
}
}
return [2 /*return*/, str];
case 11: return [3 /*break*/, 27];
case 12:
if (!(cmdName === 'EXEC')) return [3 /*break*/, 15];
if (!!(0, reportUtils_1.isLoopExploring)(ctx)) return [3 /*break*/, 14];
return [4 /*yield*/, (0, jsSandbox_1.runUserJsAndGetRaw)(data, cmdRest, ctx)];
case 13:
_b.sent();
_b.label = 14;
case 14: return [3 /*break*/, 27];
case 15:
if (!(cmdName === 'IMAGE')) return [3 /*break*/, 18];
if (!!(0, reportUtils_1.isLoopExploring)(ctx)) return [3 /*break*/, 17];
return [4 /*yield*/, (0, jsSandbox_1.runUserJsAndGetRaw)(data, cmdRest, ctx)];
case 16:
img = _b.sent();
if (img != null) {
try {
processImage(ctx, img);
}
catch (e) {
if (!(0, errors_1.isError)(e))
throw e;
throw new errors_1.ImageError(e, cmd);
}
}
_b.label = 17;
case 17: return [3 /*break*/, 27];
case 18:
if (!(cmdName === 'LINK')) return [3 /*break*/, 22];
if (!!(0, reportUtils_1.isLoopExploring)(ctx)) return [3 /*break*/, 21];
return [4 /*yield*/, (0, jsSandbox_1.runUserJsAndGetRaw)(data, cmdRest, ctx)];
case 19:
pars = _b.sent();
if (!(pars != null)) return [3 /*break*/, 21];
return [4 /*yield*/, processLink(ctx, pars)];
case 20:
_b.sent();
_b.label = 21;
case 21: return [3 /*break*/, 27];
case 22:
if (!(cmdName === 'HTML')) return [3 /*break*/, 26];
if (!!(0, reportUtils_1.isLoopExploring)(ctx)) return [3 /*break*/, 25];
return [4 /*yield*/, (0, jsSandbox_1.runUserJsAndGetRaw)(data, cmdRest, ctx)];
case 23:
html = _b.sent();
if (!(html != null)) return [3 /*break*/, 25];
return [4 /*yield*/, processHtml(ctx, html)];
case 24:
_b.sent();
_b.label = 25;
case 25: return [3 /*break*/, 27];
case 26: throw new errors_1.CommandSyntaxError(cmd);
case 27: return [2 /*return*/];
case 28:
err_1 = _b.sent();
if (!(0, errors_1.isError)(err_1))
throw err_1;
if (ctx.options.errorHandler != null) {
return [2 /*return*/, ctx.options.errorHandler(err_1)];
}
return [2 /*return*/, err_1];
case 29: return [2 /*return*/];
}
});
}); };
var builtInRegexes = types_1.BUILT_IN_COMMANDS.map(function (word) { return new RegExp("^".concat(word, "\\b")); });
var notBuiltIns = function (cmd) {
return !builtInRegexes.some(function (r) { return r.test(cmd.toUpperCase()); });
};
function getCommand(command, shorthands, fixSmartQuotes) {
// Get a cleaned version of the command
var cmd = command.trim();
if (cmd[0] === '*') {
var aliasName = cmd.slice(1).trim();
if (!shorthands[aliasName])
throw new errors_1.InvalidCommandError('Unknown alias', cmd);
cmd = shorthands[aliasName];
debug_1.logger.debug("Alias for: ".concat(cmd));
}
else if (cmd[0] === '=') {
cmd = "INS ".concat(cmd.slice(1).trim());
}
else if (cmd[0] === '!') {
cmd = "EXEC ".concat(cmd.slice(1).trim());
}
else if (notBuiltIns(cmd)) {
cmd = "INS ".concat(cmd.trim());
}
//replace 'smart' quotes with straight quotes
if (fixSmartQuotes) {
cmd = cmd
.replace(/[\u201C\u201D\u201E]/g, '"')
.replace(/[\u2018\u2019\u201A]/g, "'");
}
return cmd.trim();
}
exports.getCommand = getCommand;
function splitCommand(cmd) {
// Extract command name
var cmdNameMatch = /^(\S+)\s*/.exec(cmd);
var cmdName;
var cmdRest = '';
if (cmdNameMatch != null) {
cmdName = cmdNameMatch[1].toUpperCase();
cmdRest = cmd.slice(cmdName.length).trim();
}
return { cmdName: cmdName, cmdRest: cmdRest };
}
exports.splitCommand = splitCommand;
// ==========================================
// Individual commands
// ==========================================
var processForIf = function (data, node, ctx, cmd, cmdName, cmdRest) { return __awaiter(void 0, void 0, void 0, function () {
var isIf, forMatch, varName, curLoop, parentPorTrNode, parentPorTrNodeTag, parentLoopLevel, fParentIsExploring, loopOver, shouldRun;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
isIf = cmdName === 'IF';
forMatch = null;
varName = undefined;
if (isIf) {
if (!node._ifName) {
node._ifName = "__if_".concat(ctx.gCntIf);
ctx.gCntIf += 1;
}
varName = node._ifName;
}
else {
forMatch = /^(\S+)\s+IN\s+(.+)/i.exec(cmdRest);
if (!forMatch)
throw new errors_1.InvalidCommandError('Invalid FOR command', cmd);
varName = forMatch[1];
}
curLoop = (0, reportUtils_1.getCurLoop)(ctx);
if (!!(curLoop && curLoop.varName === varName)) return [3 /*break*/, 6];
// Check whether we already started a nested IF without and END-IF for this p or tr tag
if (isIf) {
parentPorTrNode = findParentPorTrNode(node);
parentPorTrNodeTag = parentPorTrNode != null
? parentPorTrNode._fTextNode
? null
: parentPorTrNode._tag
: null;
if (parentPorTrNode != null) {
if (parentPorTrNodeTag === 'w:p') {
if (ctx.pIfCheckMap.has(parentPorTrNode) &&
ctx.pIfCheckMap.get(parentPorTrNode) !== cmd)
throw new errors_1.InvalidCommandError('Invalid IF command nested into another IF command on the same line', cmd);
else
ctx.pIfCheckMap.set(parentPorTrNode, cmd);
}
else if (parentPorTrNodeTag === 'w:tr') {
if (ctx.trIfCheckMap.has(parentPorTrNode) &&
ctx.trIfCheckMap.get(parentPorTrNode) !== cmd)
throw new errors_1.InvalidCommandError('Invalid IF command nested into another IF command on the same table row', cmd);
else
ctx.trIfCheckMap.set(parentPorTrNode, cmd);
}
}
}
parentLoopLevel = ctx.loops.length - 1;
fParentIsExploring = parentLoopLevel >= 0 && ctx.loops[parentLoopLevel].idx === -1;
loopOver = void 0;
if (!fParentIsExploring) return [3 /*break*/, 1];
loopOver = [];
return [3 /*break*/, 5];
case 1:
if (!isIf) return [3 /*break*/, 3];
return [4 /*yield*/, (0, jsSandbox_1.runUserJsAndGetRaw)(data, cmdRest, ctx)];
case 2:
shouldRun = !!(_a.sent());
loopOver = shouldRun ? [1] : [];
return [3 /*break*/, 5];
case 3:
if (!forMatch)
throw new errors_1.InvalidCommandError('Invalid FOR command', cmd);
return [4 /*yield*/, (0, jsSandbox_1.runUserJsAndGetRaw)(data, forMatch[2], ctx)];
case 4:
loopOver = _a.sent();
if (!Array.isArray(loopOver))
throw new errors_1.InvalidCommandError('Invalid FOR command (can only iterate over Array)', cmd);
_a.label = 5;
case 5:
ctx.loops.push({
refNode: node,
refNodeLevel: ctx.level,
varName: varName,
loopOver: loopOver,
isIf: isIf,
// run through the loop once first, without outputting anything
// (if we don't do it like this, we could not run empty loops!)
idx: -1,
});
_a.label = 6;
case 6:
(0, reportUtils_1.logLoop)(ctx.loops);
return [2 /*return*/];
}
});
}); };
var processEndForIf = function (node, ctx, cmd, cmdName, cmdRest) {
var isIf = cmdName === 'END-IF';
var curLoop = (0, reportUtils_1.getCurLoop)(ctx);
if (!curLoop)
throw new errors_1.InvalidCommandError("Unexpected ".concat(cmdName, " outside of ").concat(isIf ? 'IF statement' : 'FOR loop', " context"), cmd);
// Reset the if check flag for the corresponding p or tr parent node
var parentPorTrNode = findParentPorTrNode(node);
var parentPorTrNodeTag = parentPorTrNode != null
? parentPorTrNode._fTextNode
? null
: parentPorTrNode._tag
: null;
if (parentPorTrNodeTag === 'w:p') {
ctx.pIfCheckMap.delete(parentPorTrNode);
}
else if (parentPorTrNodeTag === 'w:tr') {
ctx.trIfCheckMap.delete(parentPorTrNode);
}
// First time we visit an END-IF node, we assign it the arbitrary name
// generated when the IF was processed
if (isIf && !node._ifName) {
node._ifName = curLoop.varName;
ctx.gCntEndIf += 1;
}
// Check if this is the expected END-IF/END-FOR. If not:
// - If it's one of the nested varNames, throw
// - If it's not one of the nested varNames, ignore it; we find
// cases in which an END-IF/FOR is found that belongs to a previous
// part of the paragraph of the current loop.
var varName = isIf ? node._ifName : cmdRest;
if (curLoop.varName !== varName) {
if (ctx.loops.find(function (o) { return o.varName === varName; }) == null) {
debug_1.logger.debug("Ignoring ".concat(cmd, " (").concat(varName, ", but we're expecting ").concat(curLoop.varName, ")"));
return;
}
throw new errors_1.InvalidCommandError('Invalid command', cmd);
}
// Get the next item in the loop
var nextIdx = curLoop.idx + 1;
var nextItem = curLoop.loopOver[nextIdx];
if (nextItem != null) {
// next iteration
ctx.vars[varName] = nextItem;
ctx.fJump = true;
curLoop.idx = nextIdx;
}
else {
// loop finished
ctx.loops.pop();
}
};
var imageToContext = function (ctx, img) {
validateImage(img);
ctx.imageAndShapeIdIncrement += 1;
var id = String(ctx.imageAndShapeIdIncrement);
var relId = "img".concat(id);
ctx.images[relId] = img;
return relId;
};
function validateImage(img) {
if (!(img.data instanceof Uint8Array ||
img.data instanceof ArrayBuffer ||
typeof img.data === 'string')) {
throw new Error('image .data property needs to be provided as Uint8Array (e.g. Buffer), ArrayBuffer, or as a base64-encoded string');
}
if (!types_1.ImageExtensions.includes(img.extension)) {
throw new Error("An extension (one of ".concat(types_1.ImageExtensions, ") needs to be provided when providing an image or a thumbnail."));
}
}
function validateImagePars(pars) {
if (!Number.isFinite(pars.width))
throw new Error("invalid image width: ".concat(pars.width, " (in cm)"));
if (!Number.isFinite(pars.height))
throw new Error("invalid image height: ".concat(pars.height, " (in cm)"));
validateImage(pars);
if (pars.thumbnail)
validateImage(pars.thumbnail);
}
var processImage = function (ctx, imagePars) {
var _a;
validateImagePars(imagePars);
var cx = (imagePars.width * 360e3).toFixed(0);
var cy = (imagePars.height * 360e3).toFixed(0);
var imgRelId = imageToContext(ctx, getImageData(imagePars));
var id = String(ctx.imageAndShapeIdIncrement);
var alt = imagePars.alt || 'desc';
var node = reportUtils_1.newNonTextNode;
var extNodes = [];
extNodes.push(node('a:ext', { uri: '{28A0092B-C50C-407E-A947-70E740481C1C}' }, [
node('a14:useLocalDpi', {
'xmlns:a14': 'http://schemas.microsoft.com/office/drawing/2010/main',
val: '0',
}),
]));
// http://officeopenxml.com/drwSp-rotate.php
// Values are in 60,000ths of a degree, with positive angles moving clockwise or towards the positive y-axis.
var rot = imagePars.rotation
? (imagePars.rotation * 60e3).toString()
: undefined;
if (ctx.images[imgRelId].extension === '.svg') {
// Default to an empty thumbnail, as it is not critical and just part of the docx standard's scaffolding.
// Without a thumbnail, the svg won't render (even in newer versions of Word that don't need the thumbnail).
var thumbnail = (_a = imagePars.thumbnail) !== null && _a !== void 0 ? _a : {
data: 'bm90aGluZwo=',
extension: '.png',
};
var thumbRelId = imageToContext(ctx, thumbnail);
extNodes.push(node('a:ext', { uri: '{96DAC541-7B7A-43D3-8B79-37D633B846F1}' }, [
node('asvg:svgBlip', {
'xmlns:asvg': 'http://schemas.microsoft.com/office/drawing/2016/SVG/main',
'r:embed': imgRelId,
}),
]));
// For SVG the thumb is placed where the image normally goes.
imgRelId = thumbRelId;
}
var pic = node('pic:pic', { 'xmlns:pic': 'http://schemas.openxmlformats.org/drawingml/2006/picture' }, [
node('pic:nvPicPr', {}, [
node('pic:cNvPr', { id: '0', name: "Picture ".concat(id), descr: alt }),
node('pic:cNvPicPr', {}, [
node('a:picLocks', { noChangeAspect: '1', noChangeArrowheads: '1' }),
]),
]),
node('pic:blipFill', {}, [
node('a:blip', { 'r:embed': imgRelId, cstate: 'print' }, [
node('a:extLst', {}, extNodes),
]),
node('a:srcRect'),
node('a:stretch', {}, [node('a:fillRect')]),
]),
node('pic:spPr', { bwMode: 'auto' }, [
node('a:xfrm', rot ? { rot: rot } : {}, [
node('a:off', { x: '0', y: '0' }),
node('a:ext', { cx: cx, cy: cy }),
]),
node('a:prstGeom', { prst: 'rect' }, [node('a:avLst')]),
node('a:noFill'),
node('a:ln', {}, [node('a:noFill')]),
]),
]);
var drawing = node('w:drawing', {}, [
node('wp:inline', { distT: '0', distB: '0', distL: '0', distR: '0' }, [
node('wp:extent', { cx: cx, cy: cy }),
node('wp:docPr', { id: id, name: "Picture ".concat(id), descr: alt }),
node('wp:cNvGraphicFramePr', {}, [
node('a:graphicFrameLocks', {
'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
noChangeAspect: '1',
}),
]),
node('a:graphic', { 'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main' }, [
node('a:graphicData', { uri: 'http://schemas.openxmlformats.org/drawingml/2006/picture' }, [pic]),
]),
]),
]);
ctx.pendingImageNode = { image: drawing };
if (imagePars.caption) {
ctx.pendingImageNode.caption = [
node('w:br'),
node('w:t', {}, [(0, reportUtils_1.newTextNode)(imagePars.caption)]),
];
}
};
function getImageData(imagePars) {
var data = imagePars.data, extension = imagePars.extension;
if (!extension) {
throw new Error('If you return image `data`, make sure you return an extension as well!');
}
return { extension: extension, data: data };
}
var processLink = function (ctx, linkPars) { return __awaiter(void 0, void 0, void 0, function () {
var url, _a, label, id, relId, node, textRunPropsNode, link;
return __generator(this, function (_b) {
url = linkPars.url, _a = linkPars.label, label = _a === void 0 ? url : _a;
ctx.linkId += 1;
id = String(ctx.linkId);
relId = "link".concat(id);
ctx.links[relId] = { url: url };
node = reportUtils_1.newNonTextNode;
textRunPropsNode = ctx.textRunPropsNode;
link = node('w:hyperlink', { 'r:id': relId, 'w:history': '1' }, [
node('w:r', {}, [
textRunPropsNode ||
node('w:rPr', {}, [node('w:u', { 'w:val': 'single' })]),
node('w:t', {}, [(0, reportUtils_1.newTextNode)(label)]),
]),
]);
ctx.pendingLinkNode = link;
return [2 /*return*/];
});
}); };
var processHtml = function (ctx, data) { return __awaiter(void 0, void 0, void 0, function () {
var id, relId, node, html;
return __generator(this, function (_a) {
ctx.htmlId += 1;
id = String(ctx.htmlId);
relId = "html".concat(id);
ctx.htmls[relId] = data;
node = reportUtils_1.newNonTextNode;
html = node('w:altChunk', { 'r:id': relId });
ctx.pendingHtmlNode = html;
return [2 /*return*/];
});
}); };
// ==========================================
// Helpers
// ==========================================
var BufferKeys = ['w:p', 'w:tr', 'w:tc'];
var appendTextToTagBuffers = function (text, ctx, options) {
if (ctx.fSeekQuery)
return;
var fCmd = options.fCmd, fInsertedText = options.fInsertedText;
var type = fCmd ? 'cmds' : 'text';
BufferKeys.forEach(function (key) {
var buf = ctx.buffers[key];
buf[type] += text;
if (fInsertedText)
buf.fInsertedText = true;
});
};
function updateID(newNode, ctx) {
ctx.imageAndShapeIdIncrement += 1;
var id = String(ctx.imageAndShapeIdIncrement);
newNode._attrs = __assign(__assign({}, newNode._attrs), { id: "".concat(id) });
}