const C = { textbox: 'textbox', funnybox: 'funnybox', nosize: 'nosize', nowidth: 'nowidth', notificationSpecParsed: 'notificationSpecParsed', rightAngleLine: 'right-angle-line', } registerParser(C.textbox, parserTextbox) registerLayoutProducer(C.textbox, layoutTextbox) registerRenderer(C.textbox, renderTextbox) registerParser(C.funnybox, parserFunnybox) registerParser(C.nosize, parserNosize) registerRenderer(C.nosize, renderNosize) registerParser(C.nowidth, parserNowidth) registerParser(C.rightAngleLine, parserRightAngleLine) registerLayoutProducer(C.rightAngleLine, layoutRightAngleLine) registerRenderer(C.rightAngleLine, renderRightAngleLine) addListener(C.notificationSpecParsed, specParsed) function parserTextbox(line, inputFile, variables, settings) { const {key, tokens, contentLines, content} = parseKeyContent(line, inputFile, 3, variables, true) const params = {} let width = settings['text-width'] let height = settings['text-height'] if (tokens[0] !== undefined && tokens[1] !== undefined && (!isNaN(tokens[0]) || tokens[0] === 'fill') && (!isNaN(tokens[1]) || tokens[1] === 'fill')) { width = fillOrFloat(Util.extractParams(tokens[0], params, 'fillWidth'), inputFile, true) height = fillOrFloat(Util.extractParams(tokens[1], params, 'fillHeight'), inputFile, true) let firstLineArr = contentLines[0].split(' ') let count = 0 while (true) { // Remove the size tokens from contentLines let token = firstLineArr.shift() if (token != '') { count++ } if (count === 2) { break } } contentLines[0] = firstLineArr.join(' ') } return { key, type: C.textbox, width: fillOrFloat(Util.extractParams(tokens[0], params, 'fillWidth'), inputFile, true), height: fillOrFloat(Util.extractParams(tokens[1], params, 'fillHeight'), inputFile, true), params, text: contentLines, } } function layoutTextbox(obj, position) { const {colX, rowY, colWidth, rowHeight} = position const x1 = colX + colWidth / 2 - obj.width / 2 const y1 = rowY + rowHeight / 2 - obj.height / 2 return { cx: x1 + obj.width / 2, cy: y1 + obj.height / 2, width: obj.width, height: obj.height, x1: x1, y1: y1, } } function renderTextbox(obj, sizeAndPosition, styleBlock, svgBlock, context, styleData) { let {x1, y1, width, height} = sizeAndPosition let buf = `` buf += renderText(obj, sizeAndPosition, styleBlock, svgBlock, context, styleData) return buf } function renderText(obj, sizeAndPosition, styleBlock, svgBlock, context, styleData) { let {cx, cy, width, height, gridAlign} = sizeAndPosition styleBlock = applyTextAlignment(gridAlign, styleData) let x = cx let y = cy if (sizeAndPosition.gridAlign.horizontal === 'left') { x -= width / 2 } else if (sizeAndPosition.gridAlign.horizontal === 'right') { x += width / 2 } if (sizeAndPosition.gridAlign.vertical === 'top') { y -= height / 2 } else if (sizeAndPosition.gridAlign.vertical === 'bottom') { y += height / 2 } let lineHeight = 1.2 let dy = 0 if (sizeAndPosition.gridAlign.vertical === 'middle') { dy = round(-1 * lineHeight / 2 * (obj.text.length - 1)) } else if (sizeAndPosition.gridAlign.vertical === 'bottom') { dy = round(-1 * lineHeight * (obj.text.length - 1)) } // Any styles defined will override default class let buf = `` for (var line of obj.text) { const safe = Util.htmlEntities(line.trim()) buf += `${safe}` dy = lineHeight } buf += `` return buf } function applyTextAlignment(gridAlign, styleData) { let alignClass if (gridAlign.vertical === 'top') { alignClass = `-pln-diagram-at` } else if (gridAlign.vertical === 'bottom') { alignClass = `-pln-diagram-ab` } else if (gridAlign.vertical === 'middle') { alignClass = `-pln-diagram-am` } if (gridAlign.horizontal === 'left') { alignClass += ` -pln-diagram-al` } else if (gridAlign.horizontal === 'right') { alignClass += ` -pln-diagram-ar` } else if (gridAlign.horizontal === 'center') { alignClass += ` -pln-diagram-ac` } let classesBuf = '' for (var c of styleData.classes) { classesBuf += ' ' + c } let styleBuf = '' if (styleData.style !== '') { styleBuf = ` style="${styleData.style}"` } return ` class="${alignClass}${classesBuf}"${styleBuf}` } function parserFunnybox(line, inputFile, variables, settings) { const {key, tokens, contentLines, content} = parseKeyContent(line, inputFile, 3, variables, true) return { key, type: C.funnybox, } } function parserNosize(line, inputFile, variables, settings) { const {key, tokens, contentLines, content} = parseKeyContent(line, inputFile, 3, variables, true) return { key, width: 1, type: C.nosize, } } function renderNosize(obj, sizeAndPosition, styleBlock, svgBlock) { let {x1, y1, width, height} = sizeAndPosition let buf = `` return buf } function parserNowidth(line, inputFile, variables, settings) { const {key, tokens, contentLines, content} = parseKeyContent(line, inputFile, 3, variables, true) return { key, height: 1, type: C.nosize, } } function parserRightAngleLine(line, inputFile, variables) { const {key, tokens} = parseKeyContent(line, inputFile, 2, variables, true) const params = { fillWidth: [0, 1], fillHeight: [0, 1], } return { key, type: C.rightAngleLine, width: 'fill', height: 'fill', params } } function layoutRightAngleLine(obj, position) { const {colX, rowY, colWidth, rowHeight} = position const pathHeadUp = 'l 5 0 l -5 -15 l -5 15 z' const pathHeadDown = 'l 5 0 l -5 15 l -5 -15 z' const pathHeadLeft = 'l 0 5 l -15 -5 l 15 -5 z' const pathHeadRight = 'l 0 5 l 15 -5 l -15 -5 z' let pathHead let x1 = position.box.from.x let y1 = position.box.from.y let x2 = position.box.to.x let y2 = position.box.to.y let dx1 = 0 let dy1 = 0 let dx2 = 0 let dy2 = 0 if (position.box.to.handleV === 'bottom') { y2 += 16 pathHead = pathHeadUp dx1 = x2 - x1 dy2 = y2 - y1 } else if (position.box.to.handleV === 'top') { y2 -= 16 pathHead = pathHeadDown dx1 = x2 - x1 dy2 = y2 - y1 } else if (position.box.to.handleH === 'left') { x2 -= 16 pathHead = pathHeadRight dy1 = y2 - y1 dx2 = x2 - x1 } else if (position.box.to.handleH === 'right') { x2 += 16 pathHead = pathHeadLeft dy1 = y2 - y1 dx2 = x2 - x1 } let path = `m ${x1} ${y1} l ${dx1} ${dy1} l ${dx2} ${dy2}` pathHead = `m ${x2} ${y2} ${pathHead}` return { path, pathHead, } } function renderRightAngleLine(obj, sizeAndPosition, styleBlock, svgBlock) { const {cx, cy} = sizeAndPosition const x1 = cx - obj.width / 2 const y1 = cy - obj.height / 2 let pathHeadStyle = ` class="${obj.key}-head"` const pathSpec = `M ${round(x1)},${round(y1)} ${sizeAndPosition.path}` let buf = `` const headPathSpec = `M ${round(x1)},${round(y1)} ${sizeAndPosition.pathHead}` buf += `` return buf } function specParsed(spec) { spec.globalStyles['.test-spec-parsed'] = `text-anchor: start;` }