UNPKG

@npmstuff/argdown-core

Version:

A pluggable parser for the Argdown argumentation syntax

380 lines 15.9 kB
"use strict"; 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