@npmstuff/argdown-core
Version:
A pluggable parser for the Argdown argumentation syntax
380 lines • 15.9 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DotExportPlugin = void 0;
const ArgdownPluginError_1 = require("../ArgdownPluginError");
const model_1 = require("../model/model");
const utils_1 = require("../utils");
const utils_2 = require("../utils");
const lodash_defaultsdeep_1 = __importDefault(require("lodash.defaultsdeep"));
const lodash_merge_1 = __importDefault(require("lodash.merge"));
const defaultSettings = {
useHtmlLabels: true,
graphname: "Argument Map",
mapBgColor: "transparent",
measureLineWidth: false,
group: utils_1.ensure.object({
lineWidth: 400,
charactersInLine: 80,
font: "arial",
fontSize: 12,
bold: false,
margin: "8"
}),
closedGroup: utils_1.ensure.object({
lineWidth: 400,
charactersInLine: 80,
font: "arial",
fontSize: 12,
bold: false,
margin: "0.2"
}),
argument: utils_1.ensure.object({
lineWidth: 180,
minWidth: 180,
margin: "0.11,0.055",
shape: "box",
style: "filled, rounded",
title: utils_1.ensure.object({
font: "arial",
fontSize: 10,
bold: true,
charactersInLine: 40
}),
text: utils_1.ensure.object({
font: "arial",
fontSize: 10,
bold: false,
charactersInLine: 40
}),
images: utils_1.ensure.object({
position: "top",
padding: 0
})
}),
statement: utils_1.ensure.object({
lineWidth: 180,
minWidth: 180,
margin: "0.11,0.055",
shape: "box",
style: "filled,rounded,bold",
title: utils_1.ensure.object({
font: "arial",
fontSize: 10,
bold: true,
charactersInLine: 40
}),
text: utils_1.ensure.object({
font: "arial",
fontSize: 10,
bold: false,
charactersInLine: 40
}),
images: utils_1.ensure.object({
position: "top",
padding: 0
})
}),
edge: utils_1.ensure.object({
penWidth: 1,
arrowSize: 1
}),
graphVizSettings: utils_1.ensure.object({
rankdir: "BT",
concentrate: "false",
ratio: "auto",
size: "10,10"
}),
sameRank: utils_1.ensure.array([])
};
class DotExportPlugin {
constructor(config) {
this.name = "DotExportPlugin";
this.prepare = (request, response) => {
(0, ArgdownPluginError_1.checkResponseFields)(this, response, [
"statements",
"arguments",
"map",
"relations"
]);
let settings = this.getSettings(request);
(0, utils_1.mergeDefaults)(settings, defaultSettings);
};
this.run = (request, response) => {
var _a, _b;
const settings = this.getSettings(request);
let rankMap = {};
rankMap = Object.values(response.arguments).reduce(reduceToRankMap, rankMap);
rankMap = Object.values(response.statements).reduce(reduceToRankMap, rankMap);
settings.sameRank.push(...Object.values(rankMap));
response.groupCount = 0;
let dot = `digraph "${settings.graphname}" {\n\n`;
if (settings.graphVizSettings) {
const keys = Object.keys(settings.graphVizSettings);
for (let key of keys) {
const value = settings.graphVizSettings[key];
dot += key + ' = "' + value + '";\n';
}
}
dot += `edge[arrowsize="${(_a = settings.edge) === null || _a === void 0 ? void 0 : _a.arrowSize}", penwidth="${(_b = settings.edge) === null || _b === void 0 ? void 0 : _b.penWidth}"]`;
dot += `graph [bgcolor = "${settings.mapBgColor}" ]`;
for (let node of response.map.nodes) {
dot += this.exportNodesRecursive(node, request, response, settings);
}
dot += "\n\n";
const edges = response.map.edges;
for (let edge of edges) {
let attributes = `type="${edge.relationType}", `;
attributes += `color="${edge.color}"`;
switch (edge.relationType) {
case model_1.RelationType.CONTRARY:
attributes += `, dir="both"`;
break;
case model_1.RelationType.CONTRADICTORY:
attributes += `, dir="both", arrowtail="diamond", arrowhead="diamond"`;
break;
}
dot += ` ${edge.from.id} -> ${edge.to.id} [${attributes}];\n`;
}
if (settings.sameRank && settings.sameRank.length > 0) {
const nodeMaps = getNodeIdsMaps(response.map);
for (let rank of settings.sameRank) {
dot += `{ rank = same;\n`;
for (let argumentTitle of rank.arguments) {
const id = nodeMaps.argumentNodes[argumentTitle];
if (!id) {
continue;
}
dot += `${id};\n`;
}
for (let ecTitle of rank.statements) {
const id = nodeMaps.statementNodes[ecTitle];
if (!id) {
continue;
}
dot += `${id};\n`;
}
dot += `};\n`;
}
}
dot += "\n}";
response.dot = dot;
return response;
};
this.defaults = (0, lodash_defaultsdeep_1.default)({}, config, defaultSettings);
}
getSettings(request) {
if ((0, utils_1.isObject)(request.dot)) {
const settings = request.dot;
return settings;
}
else {
request.dot = {};
return request.dot;
}
}
exportNodesRecursive(node, request, response, settings) {
let dot = "";
response.groupCount =
response.groupCount === undefined ? 0 : response.groupCount;
if (node.type === model_1.ArgdownTypes.GROUP_MAP_NODE) {
const groupNode = node;
response.groupCount++;
let dotGroupId = "cluster_" + response.groupCount;
let groupLabel = node.labelTitle || "";
const groupSettings = groupNode.isClosed
? settings.closedGroup
: settings.group;
if (settings.useHtmlLabels) {
groupLabel = settings.measureLineWidth
? addLineBreaksAndEscape(groupLabel, true, {
maxWidth: groupSettings.lineWidth,
fontSize: groupSettings.fontSize,
bold: groupSettings.bold,
font: groupSettings.font
})
: addLineBreaksAndEscape(groupLabel, false, {
charactersInLine: groupSettings.charactersInLine
});
groupLabel = `<<FONT FACE="${groupSettings
.font}" POINT-SIZE="${groupSettings.fontSize}" COLOR="${node.fontColor}">${groupLabel}</FONT>>`;
}
else {
groupLabel = `"${escapeQuotesForDot(groupLabel)}"`;
}
let groupColor = node.color || "#CCCCCC";
if (groupNode.isClosed) {
dot += ` ${node.id} [label=${groupLabel}, shape="box", margin="${groupSettings.margin}", style="filled", penwidth="0" fillcolor="${groupColor}", fontcolor="${node.fontColor}", type="${node.type}"];\n`;
}
else {
dot += `\nsubgraph ${dotGroupId} {\n label = ${groupLabel};\n color = "${groupColor}";\n margin="${groupSettings.margin}" style = filled;\n`;
let labelloc = "t";
if (settings.graphVizSettings &&
settings.graphVizSettings.rankdir == "BT") {
labelloc = "b";
}
dot += ` labelloc = "${labelloc}";\n\n`;
if (groupNode.children) {
for (let child of groupNode.children) {
dot += this.exportNodesRecursive(child, request, response, settings);
}
}
dot += `\n}\n\n`;
}
return dot;
}
let label = "";
let color = node.color && (0, utils_1.validateColorString)(node.color) ? node.color : "#63AEF2";
const imageSettings = request.images || {};
imageSettings.files = imageSettings.files || {};
label = getLabel(node, settings, imageSettings);
if (node.type === model_1.ArgdownTypes.ARGUMENT_MAP_NODE) {
const shape = settings.argument.shape;
const widthProp = label == `""` ? `, width="${settings.argument.minWidth}"` : "";
dot += ` ${node.id} [label=${label}, margin="${settings.argument.margin}", shape="${shape}", style="${settings.argument.style}", fillcolor="${color}", fontcolor="${node.fontColor}", type="${node.type}"${widthProp}];\n`;
}
else if (node.type === model_1.ArgdownTypes.STATEMENT_MAP_NODE) {
const shape = settings.statement.shape;
const widthProp = label == `""` ? `, width="${settings.statement.minWidth}"` : "";
dot += ` ${node.id} [label=${label}, shape="${shape}", margin="${settings.statement.margin}", style="${settings.statement.style}", color="${color}", fillcolor="white", labelfontcolor="white", fontcolor="${node.fontColor}", type="${node.type}"${widthProp}];\n`;
}
return dot;
}
}
exports.DotExportPlugin = DotExportPlugin;
const addLineBreaksAndEscape = (str, measurePixelWidth, options) => {
const result = (0, utils_2.addLineBreaks)(str, measurePixelWidth, (0, lodash_merge_1.default)({
lineBreak: "<BR/>",
escapeAsHtmlEntities: true
}, options));
return result.text;
};
const escapeQuotesForDot = (str) => {
return str.replace(/\"/g, '\\"');
};
const getLabel = (node, settings, imageSettings) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const isArgumentNode = node.type === model_1.ArgdownTypes.ARGUMENT_MAP_NODE;
const title = node.labelTitle;
const text = node.labelText;
const color = node.fontColor;
let label = "";
if ((0, utils_1.stringIsEmpty)(title) && (0, utils_1.stringIsEmpty)(text)) {
return `""`;
}
if (settings.useHtmlLabels) {
const maxLineWidth = isArgumentNode
? settings.argument.lineWidth
: settings.statement.lineWidth;
const minNodeWidth = isArgumentNode
? settings.argument.minWidth
: settings.statement.minWidth;
const imagesPosition = isArgumentNode
? (_b = (_a = settings.argument) === null || _a === void 0 ? void 0 : _a.images) === null || _b === void 0 ? void 0 : _b.position
: (_d = (_c = settings.statement) === null || _c === void 0 ? void 0 : _c.images) === null || _d === void 0 ? void 0 : _d.position;
const imagesPadding = isArgumentNode
? (_f = (_e = settings.argument) === null || _e === void 0 ? void 0 : _e.images) === null || _f === void 0 ? void 0 : _f.padding
: (_h = (_g = settings.statement) === null || _g === void 0 ? void 0 : _g.images) === null || _h === void 0 ? void 0 : _h.padding;
label += `<<TABLE WIDTH="${minNodeWidth}" ALIGN="CENTER" BORDER="0" CELLSPACING="0">`;
let img = "";
if (node.images) {
if (node.images.length == 1) {
img = `<TR><TD ALIGN="CENTER"><IMG SCALE="true" ALIGN="CENTER" SRC="${imageSettings.files[node.images[0]].path}"/></TD></TR>`;
}
else if (node.images.length > 1) {
img = `<TR><TD><TABLE ALIGN="CENTER" BORDER="0" CELLSPACING="${imagesPadding}"><TR>${(_j = node.images) === null || _j === void 0 ? void 0 : _j.map(image => `<TD><IMG SRC="${imageSettings.files[image].path}"/></TD>`).join("")}</TR></TABLE></TD></TR>`;
}
}
if (imagesPosition == "top") {
label += img;
}
if (!(0, utils_1.stringIsEmpty)(title)) {
let { fontSize, font, bold, charactersInLine } = isArgumentNode
? settings.argument.title
: settings.statement.title;
let titleLabel = settings.measureLineWidth
? addLineBreaksAndEscape(title, true, {
maxWidth: maxLineWidth,
fontSize,
bold,
font,
applyRanges: node.labelTitleRanges
})
: addLineBreaksAndEscape(title, false, {
charactersInLine,
applyRanges: node.labelTitleRanges
});
if (bold) {
titleLabel = `<B>${titleLabel}</B>`;
}
titleLabel = `<TR><TD WIDTH="${minNodeWidth}" ALIGN="TEXT" BALIGN="CENTER"><FONT FACE="${font}" POINT-SIZE="${fontSize}" COLOR="${color}">${titleLabel}</FONT></TD></TR>`;
label += titleLabel;
}
if (!(0, utils_1.stringIsEmpty)(text)) {
let { fontSize, font, bold, charactersInLine } = isArgumentNode
? settings.argument.text
: settings.statement.text;
let textLabel = settings.measureLineWidth
? addLineBreaksAndEscape(text, true, {
maxWidth: maxLineWidth,
fontSize,
bold,
font,
applyRanges: node.labelTextRanges
})
: addLineBreaksAndEscape(text, false, {
charactersInLine,
applyRanges: node.labelTextRanges
});
if (bold) {
textLabel = `<B>${textLabel}</B>`;
}
textLabel = `<TR><TD ALIGN="TEXT" WIDTH="${minNodeWidth}" BALIGN="CENTER"><FONT FACE="${font}" POINT-SIZE="${fontSize}" COLOR="${color}">${textLabel}</FONT></TD></TR>`;
label += textLabel;
}
if (imagesPosition == "bottom") {
label += img;
}
label += "</TABLE>>";
}
else {
label = '"' + escapeQuotesForDot(title || "Untitled") + '"';
}
return label;
};
const reduceToRankMap = (acc, curr) => {
if (curr.data && curr.data.rank) {
const rank = acc[curr.data.rank] || {
arguments: [],
statements: []
};
if (curr.type === model_1.ArgdownTypes.ARGUMENT) {
rank.arguments.push(curr.title);
}
else {
rank.statements.push(curr.title);
}
acc[curr.data.rank] = rank;
}
return acc;
};
const getNodeIdsMaps = (map) => {
const maps = { argumentNodes: {}, statementNodes: {} };
map.nodes.reduce(reduceToNodeMaps, maps);
return maps;
};
const reduceToNodeMaps = (acc, curr) => {
if ((0, model_1.isGroupMapNode)(curr) && curr.children) {
acc = curr.children.reduce(reduceToNodeMaps, acc);
}
else if (curr.type === model_1.ArgdownTypes.ARGUMENT_MAP_NODE) {
acc.argumentNodes[curr.title] = curr.id;
}
else if (curr.type === model_1.ArgdownTypes.STATEMENT_MAP_NODE) {
acc.statementNodes[curr.title] = curr.id;
}
return acc;
};
//# sourceMappingURL=DotExportPlugin.js.map
;