From 9bce2ed6adf26b3a7b3f0291bad81f3ed1d1c9a7 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Thu, 15 Jun 2017 12:32:58 +0200 Subject: [PATCH 01/11] Code cleanup, for better understanding --- .../modules/components/shared/Label.js | 78 ++++++++++++------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 09954ea8e..5e668cc92 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -807,22 +807,38 @@ class Label { let width = 0; let height = 0; let nlLines = []; - let lines = []; let k = 0; - lines.add = function(l, text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor) { + + // Note that lines is an array with methods attached + let lines = []; + + lines.add = function(l, text, width, height, values, mod) { if (this.length == l) { this[l] = { width: 0, height: 0, blocks: [] }; } - this[l].blocks.push({ text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor }); + + this[l].blocks.push({ + text, + font: values.font, + color: values.color, + width, height, + vadjust: values.vadjust, + mod, + strokeWidth: values.strokeWidth, + strokeColor: values.strokeColor + }); } + lines.accumulate = function(l, width, height) { this[l].width += width; this[l].height = height > this[l].height ? height : this[l].height; } - lines.addAndAccumulate = function(l, text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor) { - this.add(l, text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor); + + lines.addAndAccumulate = function(l, text, width, height, values, mod = "normal") { + this.add(l, text, width, height, values, mod); this.accumulate(l, width, height); } + if (this.elementOptions.label !== undefined) { let nlLines = String(this.elementOptions.label).split('\n'); let lineCount = nlLines.length; @@ -834,28 +850,32 @@ class Label { if (blocks) { if (blocks.length == 0) { let values = this.getFormattingValues(ctx, selected, hover, "normal"); - lines.addAndAccumulate(k, "", values.font, values.color, 0, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor); + lines.addAndAccumulate(k, "", 0, values.size, values); height += lines[k].height; k++; continue; } for (let j = 0; j < blocks.length; j++) { + let values = this.getFormattingValues(ctx, selected, hover, blocks[j].mod); + if (this.fontOptions.maxWdt > 0) { - let values = this.getFormattingValues(ctx, selected, hover, blocks[j].mod); let words = blocks[j].text.split(" "); let atStart = true let text = ""; let measure = { width: 0 }; let lastMeasure; + let w = 0; while (w < words.length) { let pre = atStart ? "" : " "; + let newText = text + pre + words[w]; + lastMeasure = measure; - measure = ctx.measureText(text + pre + words[w]); - if ((lineWidth + measure.width > this.fontOptions.maxWdt) && - (lastMeasure.width != 0)) { + measure = ctx.measureText(newText); + + if ((lineWidth + measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { lineHeight = (values.height > lineHeight) ? values.height : lineHeight; - lines.add(k, text, values.font, values.color, lastMeasure.width, values.height, values.vadjust, blocks[j].mod, values.strokeWidth, values.strokeColor); + lines.add(k, text, lastMeasure.width, values.height, values, blocks[j].mod); lines.accumulate(k, lastMeasure.width, lineHeight); text = ""; atStart = true; @@ -864,11 +884,11 @@ class Label { height += lines[k].height; k++; } else { - text = text + pre + words[w]; + text = newText; if (w === words.length-1) { lineHeight = (values.height > lineHeight) ? values.height : lineHeight; lineWidth += measure.width; - lines.add(k, text, values.font, values.color, measure.width, values.height, values.vadjust, blocks[j].mod, values.strokeWidth, values.strokeColor); + lines.add(k, text, measure.width, values.height, values, blocks[j].mod); lines.accumulate(k, measure.width, lineHeight); if (j === blocks.length-1) { width = lines[k].width > width ? lines[k].width : width; @@ -881,9 +901,8 @@ class Label { } } } else { - let values = this.getFormattingValues(ctx, selected, hover, blocks[j].mod); let measure = ctx.measureText(blocks[j].text); - lines.addAndAccumulate(k, blocks[j].text, values.font, values.color, measure.width, values.height, values.vadjust, blocks[j].mod, values.strokeWidth, values.strokeColor); + lines.addAndAccumulate(k, blocks[j].text, measure.width, values.height, values, blocks[j].mod); width = lines[k].width > width ? lines[k].width : width; if (blocks.length-1 === j) { height += lines[k].height; @@ -894,6 +913,14 @@ class Label { } } } else { + let addText = function(k, text, in_width, values) { + // Following var's in encompassing function scope. + // Hence the need to rename param 'in_width'. + lines.addAndAccumulate(k, text, in_width, values.size, values); + width = lines[k].width > width ? lines[k].width : width; + height += lines[k].height; + } + for (let i = 0; i < lineCount; i++) { let values = this.getFormattingValues(ctx, selected, hover, "normal"); if (this.fontOptions.maxWdt > 0) { @@ -901,23 +928,24 @@ class Label { let text = ""; let measure = { width: 0 }; let lastMeasure; + + let w = 0; while (w < words.length) { let pre = (text === "") ? "" : " "; + let newText = text + pre + words[w]; + lastMeasure = measure; - measure = ctx.measureText(text + pre + words[w]); + measure = ctx.measureText(newText); + if ((measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { - lines.addAndAccumulate(k, text, values.font, values.color, lastMeasure.width, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor) - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; + addText(k, text, lastMeasure.width, values); text = ""; k++; } else { - text = text + pre + words[w]; + text = newText; if (w === words.length-1) { - lines.addAndAccumulate(k, text, values.font, values.color, measure.width, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor) - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; + addText(k, text, measure.width, values); k++; } w++; @@ -926,9 +954,7 @@ class Label { } else { let text = nlLines[i]; let measure = ctx.measureText(text); - lines.addAndAccumulate(k, text, values.font, values.color, measure.width, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor); - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; + addText(k, text, measure.width, values); k++; } } From 5ec87dc8681903a3ea1bad3aba07378d609a79cd Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Fri, 16 Jun 2017 07:07:33 +0200 Subject: [PATCH 02/11] Further refactoring; text processing to blocks in separate method --- .../modules/components/shared/Label.js | 255 ++++++++++-------- 1 file changed, 149 insertions(+), 106 deletions(-) diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 5e668cc92..0bcb31535 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -797,16 +797,23 @@ class Label { return ((selected !== this.fontOptions.selectedState) && (hover !== this.fontOptions.hoverState)); } + /** - * This explodes the label string into lines and sets the width, height and number of lines. + * This explodes the passed text into lines and determines the width, height and number of lines. + * * @param ctx * @param selected + * @param hover + * @param {String} text the text to explode * @private */ - _processLabel(ctx, selected, hover) { + _processLabelText(ctx, selected, hover, text) { + if (this.elementOptions.label === undefined) { + return {width:0, height:0, lines: []}; + } + let width = 0; let height = 0; - let nlLines = []; let k = 0; // Note that lines is an array with methods attached @@ -838,139 +845,175 @@ class Label { this.add(l, text, width, height, values, mod); this.accumulate(l, width, height); } + // End def lines as an array with methods attached - if (this.elementOptions.label !== undefined) { - let nlLines = String(this.elementOptions.label).split('\n'); - let lineCount = nlLines.length; - if (this.elementOptions.font.multi) { - for (let i = 0; i < lineCount; i++) { - let blocks = this.splitBlocks(nlLines[i], this.elementOptions.font.multi); - let lineWidth = 0; - let lineHeight = 0; - if (blocks) { - if (blocks.length == 0) { - let values = this.getFormattingValues(ctx, selected, hover, "normal"); - lines.addAndAccumulate(k, "", 0, values.size, values); - height += lines[k].height; - k++; - continue; - } - for (let j = 0; j < blocks.length; j++) { - let values = this.getFormattingValues(ctx, selected, hover, blocks[j].mod); - - if (this.fontOptions.maxWdt > 0) { - let words = blocks[j].text.split(" "); - let atStart = true - let text = ""; - let measure = { width: 0 }; - let lastMeasure; - - let w = 0; - while (w < words.length) { - let pre = atStart ? "" : " "; - let newText = text + pre + words[w]; - - lastMeasure = measure; - measure = ctx.measureText(newText); - - if ((lineWidth + measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { - lineHeight = (values.height > lineHeight) ? values.height : lineHeight; - lines.add(k, text, lastMeasure.width, values.height, values, blocks[j].mod); - lines.accumulate(k, lastMeasure.width, lineHeight); - text = ""; - atStart = true; - lineWidth = 0; - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; - k++; - } else { - text = newText; - if (w === words.length-1) { - lineHeight = (values.height > lineHeight) ? values.height : lineHeight; - lineWidth += measure.width; - lines.add(k, text, measure.width, values.height, values, blocks[j].mod); - lines.accumulate(k, measure.width, lineHeight); - if (j === blocks.length-1) { - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; - k++; - } - } - w++; - atStart = false; - } - } - } else { - let measure = ctx.measureText(blocks[j].text); - lines.addAndAccumulate(k, blocks[j].text, measure.width, values.height, values, blocks[j].mod); - width = lines[k].width > width ? lines[k].width : width; - if (blocks.length-1 === j) { - height += lines[k].height; - k++; - } - } - } - } - } - } else { - let addText = function(k, text, in_width, values) { - // Following var's in encompassing function scope. - // Hence the need to rename param 'in_width'. - lines.addAndAccumulate(k, text, in_width, values.size, values); - width = lines[k].width > width ? lines[k].width : width; + + let nlLines = String(text).split('\n'); + let lineCount = nlLines.length; + + if (this.elementOptions.font.multi) { + // Multi-font case: styling tags active + + for (let i = 0; i < lineCount; i++) { + let blocks = this.splitBlocks(nlLines[i], this.elementOptions.font.multi); + if (blocks === undefined) continue; + + if (blocks.length === 0) { + let values = this.getFormattingValues(ctx, selected, hover, "normal"); + lines.addAndAccumulate(k, "", 0, values.size, values); height += lines[k].height; + k++; + continue; } - for (let i = 0; i < lineCount; i++) { - let values = this.getFormattingValues(ctx, selected, hover, "normal"); - if (this.fontOptions.maxWdt > 0) { - let words = nlLines[i].split(" "); + if (this.fontOptions.maxWdt > 0) { + // widthConstraint.maximum defined + + for (let j = 0; j < blocks.length; j++) { + let mod = blocks[j].mod; + let values = this.getFormattingValues(ctx, selected, hover, mod); + let words = blocks[j].text.split(" "); + let atStart = true let text = ""; let measure = { width: 0 }; let lastMeasure; - + let lineWidth = 0; + let lineHeight = 0; let w = 0; while (w < words.length) { - let pre = (text === "") ? "" : " "; + let pre = atStart ? "" : " "; let newText = text + pre + words[w]; lastMeasure = measure; measure = ctx.measureText(newText); - if ((measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { - addText(k, text, lastMeasure.width, values); + if ((lineWidth + measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { + lineHeight = (values.height > lineHeight) ? values.height : lineHeight; + lines.add(k, text, lastMeasure.width, values.height, values, mod); + lines.accumulate(k, lastMeasure.width, lineHeight); text = ""; + atStart = true; + lineWidth = 0; + width = lines[k].width > width ? lines[k].width : width; + height += lines[k].height; k++; } else { text = newText; if (w === words.length-1) { - addText(k, text, measure.width, values); - k++; + lineHeight = (values.height > lineHeight) ? values.height : lineHeight; + lineWidth += measure.width; + lines.add(k, text, measure.width, values.height, values, mod); + lines.accumulate(k, measure.width, lineHeight); + if (j === blocks.length-1) { + width = lines[k].width > width ? lines[k].width : width; + height += lines[k].height; + k++; + } } w++; + atStart = false; } } - } else { - let text = nlLines[i]; - let measure = ctx.measureText(text); - addText(k, text, measure.width, values); - k++; + } + } else { + // widthConstraint.maximum NOT defined + + for (let j = 0; j < blocks.length; j++) { + let mod = blocks[j].mod; + let values = this.getFormattingValues(ctx, selected, hover, mod); + let measure = ctx.measureText(blocks[j].text); + + lines.addAndAccumulate(k, blocks[j].text, measure.width, values.height, values, mod); + width = lines[k].width > width ? lines[k].width : width; + if (blocks.length-1 === j) { + height += lines[k].height; + k++; + } } } } + } else { + // Single-font case + + let addText = function(k, text, in_width, values) { + // Following var's in encompassing function scope. + // Hence the need to name param 'in_width'. + lines.addAndAccumulate(k, text, in_width, values.size, values); + width = lines[k].width > width ? lines[k].width : width; + height += lines[k].height; + } + + if (this.fontOptions.maxWdt > 0) { + // widthConstraint.maximum defined + + let values = this.getFormattingValues(ctx, selected, hover, "normal"); + + for (let i = 0; i < lineCount; i++) { + let words = nlLines[i].split(" "); + let text = ""; + let measure = { width: 0 }; + + let w = 0; + while (w < words.length) { + let pre = (text === "") ? "" : " "; + let newText = text + pre + words[w]; + let lastMeasure = measure; + measure = ctx.measureText(newText); + + if ((measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { + addText(k, text, lastMeasure.width, values); + text = ""; + k++; + } else { + text = newText; + if (w === words.length-1) { + addText(k, text, measure.width, values); + k++; + } + w++; + } + } + } + } else { + // widthConstraint.maximum NOT defined + + for (let i = 0; i < lineCount; i++) { + let text = nlLines[i]; + let measure = ctx.measureText(text); + addText(k, text, measure.width, values); + k++; + } + } } - if ((this.fontOptions.minWdt > 0) && (width < this.fontOptions.minWdt)) { - width = this.fontOptions.minWdt; + + return {width, height, lines: lines}; + } + + + /** + * This explodes the label string into lines and sets the width, height and number of lines. + * @param ctx + * @param selected + * @param hover + * @private + */ + _processLabel(ctx, selected, hover) { + let state = this._processLabelText(ctx, selected, hover, this.elementOptions.label); + + if ((this.fontOptions.minWdt > 0) && (state.width < this.fontOptions.minWdt)) { + state.width = this.fontOptions.minWdt; } - this.size.labelHeight = height; - if ((this.fontOptions.minHgt > 0) && (height < this.fontOptions.minHgt)) { - height = this.fontOptions.minHgt; + + this.size.labelHeight =state.height; + if ((this.fontOptions.minHgt > 0) && (state.height < this.fontOptions.minHgt)) { + state.height = this.fontOptions.minHgt; } - this.lines = lines; - this.lineCount = lines.length; - this.size.width = width; - this.size.height = height; + + this.lines = state.lines; + this.lineCount = state.lines.length; + this.size.width = state.width; + this.size.height = state.height; this.selectedState = selected; this.hoverState = hover; } From 8c13140dad8a05d40ff310b0a1197bd890f80300 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Fri, 16 Jun 2017 11:15:01 +0200 Subject: [PATCH 03/11] Added unit test for labels - tests standard text and html tags --- .../modules/components/shared/Label.js | 7 +- test/Label.test.js | 212 ++++++++++++++++++ 2 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 test/Label.test.js diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 0bcb31535..139c966ed 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -15,7 +15,7 @@ class Label { setOptions(options, allowDeletion = false) { this.elementOptions = options; - // We want to keep the font options seperated from the node options. + // We want to keep the font options separated from the node options. // The node options have to mirror the globals when they are not overruled. this.fontOptions = util.deepExtend({},options.font, true); @@ -808,7 +808,7 @@ class Label { * @private */ _processLabelText(ctx, selected, hover, text) { - if (this.elementOptions.label === undefined) { + if (text === undefined) { return {width:0, height:0, lines: []}; } @@ -944,10 +944,11 @@ class Label { height += lines[k].height; } + let values = this.getFormattingValues(ctx, selected, hover, "normal"); + if (this.fontOptions.maxWdt > 0) { // widthConstraint.maximum defined - let values = this.getFormattingValues(ctx, selected, hover, "normal"); for (let i = 0; i < lineCount; i++) { let words = nlLines[i].split(" "); diff --git a/test/Label.test.js b/test/Label.test.js new file mode 100644 index 000000000..f2289823a --- /dev/null +++ b/test/Label.test.js @@ -0,0 +1,212 @@ +var assert = require('assert') +var Label = require('../lib/network/modules/components/shared/Label').default; +var NodesHandler = require('../lib/network/modules/NodesHandler').default; + +/** + * Dummy class definitions for minimal required functionality. + */ + +class DummyContext { + measureText(text) { + return { + width: 12*text.length, + height: 14 + }; + } +} + + +class DummyLayoutEngine { + positionInitially() {} +} + + +describe('Network Label', function() { + + /** + * Retrieve options object from a NodesHandler instance + */ + function getOptions() { + var options = {}; + var body = { + functions: {}, + emitter: { + on: function() {} + } + } + + var nodesHandler = new NodesHandler(body, {}, options, new DummyLayoutEngine() ); + //console.log(JSON.stringify(nodesHandler.options, null, 2)); + + return nodesHandler.options; + } + + + /** + * Check if the returned lines and blocks are as expected. + * + * All width/height fields and font info are ignored. + * Within blocks, only the text is compared + */ + function checkBlocks(returned, expected) { + let showBlocks = () => { + return 'returned: ' + JSON.stringify(returned, null, 2) + '\n' + + 'expected: ' + JSON.stringify(expected, null, 2); + } + + assert(returned.lines.length === expected.lines.length, 'Number of lines does not match, ' + showBlocks()); + + for (let i = 0; i < returned.lines.length; ++i) { + let retLine = returned.lines[i]; + let expLine = expected.lines[i]; + + assert(retLine.blocks.length === expLine.blocks.length, 'Number of blocks does not match, ' + showBlocks()); + for (let j = 0; j < retLine.blocks.length; ++j) { + let retBlock = retLine.blocks[j]; + let expBlock = expLine.blocks[j]; + + assert(retBlock.text === expBlock.text, 'Text does not match, ' + showBlocks()); + + assert(retBlock.mod !== undefined); + if (retBlock.mod === 'normal') { + assert(expBlock.mod === undefined || expBlock.mod === 'normal', 'No mod field expected in returned, ' + showBlocks()); + } else { + assert(retBlock.mod === expBlock.mod, 'Mod fields do not match, line: ' + i + ', block: ' + j + + '; ret: ' + retBlock.mod + ', exp: ' + expBlock.mod + '\n' + showBlocks()); + } + } + } + } + + + function checkProcessedLabels(label, text, expected) { + var ctx = new DummyContext(); + + for (var i in text) { + var ret = label._processLabelText(ctx, false, false, text[i]); + //console.log(JSON.stringify(ret, null, 2)); + checkBlocks(ret, expected[i]); + } + } + + + var text = [ + "label text", + "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined", + "label\nwith\nnewlines", + "label with some multi tags", + + // Note funky spaces around \n's in following + "label with some \n multi tags\n and newlines" + ]; + + var expected = [{ + // In first item, width/height kept in for reference + width: 120, + height: 14, + lines: [{ + width: 120, + height: 14, + blocks: [{ + text: "label text", + width: 120, + height: 14, + }] + }] + }, + { + lines: [{ + blocks: [{text: "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined"}] + }] + }, + { + lines: [{ + blocks: [{text: "label"}] + }, + { + blocks: [{text: "with"}] + }, + { + blocks: [{text: "newlines"}] + }] + }, + // If multi not enabled, following passed verbatim + { + lines: [{ + blocks: [{text: "label with some multi tags"}] + }] + }, + { + lines: [{ + blocks: [{text: "label with some "}] + }, + { + blocks: [{text: " multi tags"}] + }, + { + blocks: [{text: " and newlines"}] + }] + }] + + + it('parses regular text labels', function (done) { + var label = new Label({}, getOptions()); + checkProcessedLabels(label, text, expected); + + done(); + }); + + + it('parses html labels', function (done) { + // NOTE: these are options at the node-level + var options = getOptions(options); + + // Need to set these explicitly + options.font.multi = true; // TODO: also test 'html', also test illegal value here + + var localExpected = expected.slice(0,3); + Array.prototype.push.apply(localExpected, [ + { + lines: [{ + blocks: [ + {text: "label "}, + {text: "with" , mod: 'bold'}, + {text: " "}, + {text: "some" , mod: 'mono'}, + {text: " "}, + {text: "multi ", mod: 'ital'}, + {text: "tags" , mod: 'boldital'} + ] + }] + }, + { + lines: [{ + blocks: [ + {text: "label "}, + {text: "with" , mod: 'bold'}, + {text: " "}, + {text: "some" , mod: 'mono'}, + {text: " "} + ] + }, + { + blocks: [ + {text: " "}, + {text: "multi ", mod: 'ital'}, + {text: "tags" , mod: 'boldital'} + ] + }, + { + blocks: [{text: " and newlines"}] + }] + } + ]); + //console.log(JSON.stringify(localExpected, null, 2)); + + + var label = new Label({}, options); + checkProcessedLabels(label, text, localExpected); + + done(); + }); +}); From d0dc238877af511ef3f8ef81df5f3dc4d5fd8357 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Fri, 16 Jun 2017 14:28:48 +0200 Subject: [PATCH 04/11] Labels added unit tests for markdown --- test/Label.test.js | 181 +++++++++++++++++++++++++++++---------------- 1 file changed, 117 insertions(+), 64 deletions(-) diff --git a/test/Label.test.js b/test/Label.test.js index f2289823a..fd365726f 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -2,9 +2,9 @@ var assert = require('assert') var Label = require('../lib/network/modules/components/shared/Label').default; var NodesHandler = require('../lib/network/modules/NodesHandler').default; -/** +/************************************************************** * Dummy class definitions for minimal required functionality. - */ + **************************************************************/ class DummyContext { measureText(text) { @@ -20,11 +20,17 @@ class DummyLayoutEngine { positionInitially() {} } +/************************************************************** + * End Dummy class definitions + **************************************************************/ + describe('Network Label', function() { /** * Retrieve options object from a NodesHandler instance + * + * NOTE: these are options at the node-level */ function getOptions() { var options = {}; @@ -69,7 +75,8 @@ describe('Network Label', function() { assert(retBlock.mod !== undefined); if (retBlock.mod === 'normal') { - assert(expBlock.mod === undefined || expBlock.mod === 'normal', 'No mod field expected in returned, ' + showBlocks()); + assert(expBlock.mod === undefined || expBlock.mod === 'normal', + 'No mod field expected in returned, ' + showBlocks()); } else { assert(retBlock.mod === expBlock.mod, 'Mod fields do not match, line: ' + i + ', block: ' + j + '; ret: ' + retBlock.mod + ', exp: ' + expBlock.mod + '\n' + showBlocks()); @@ -90,17 +97,37 @@ describe('Network Label', function() { } - var text = [ +/************************************************************** + * Test data + **************************************************************/ + + var normal_text = [ "label text", "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined", - "label\nwith\nnewlines", + "label\nwith\nnewlines" + ] + + var html_text = [ "label with some multi tags", // Note funky spaces around \n's in following "label with some \n multi tags\n and newlines" ]; - var expected = [{ + var markdown_text = [ + "label *with* `some` _multi *tags*_", + + // Note funky spaces around \n's in following + "label *with* `some` \n _multi *tags*_\n and newlines" + ]; + + + +/************************************************************** + * Expected Results + **************************************************************/ + + var normal_expected = [{ // In first item, width/height kept in for reference width: 120, height: 14, @@ -118,94 +145,120 @@ describe('Network Label', function() { lines: [{ blocks: [{text: "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined"}] }] - }, - { + }, { lines: [{ blocks: [{text: "label"}] - }, - { + }, { blocks: [{text: "with"}] - }, - { + }, { blocks: [{text: "newlines"}] }] - }, - // If multi not enabled, following passed verbatim - { + }] + + + var html_unchanged_expected = [{ lines: [{ blocks: [{text: "label with some multi tags"}] }] - }, - { + }, { lines: [{ blocks: [{text: "label with some "}] - }, - { + }, { blocks: [{text: " multi tags"}] - }, - { + }, { + blocks: [{text: " and newlines"}] + }] + }] + + + var markdown_unchanged_expected = [{ + lines: [{ + blocks: [{text: "label *with* `some` _multi *tags*_"}] + }] + }, { + lines: [{ + blocks: [{text: "label *with* `some` "}] + }, { + blocks: [{text: " _multi *tags*_"}] + }, { blocks: [{text: " and newlines"}] }] }] + var multi_expected = [{ + lines: [{ + blocks: [ + {text: "label "}, + {text: "with" , mod: 'bold'}, + {text: " "}, + {text: "some" , mod: 'mono'}, + {text: " "}, + {text: "multi ", mod: 'ital'}, + {text: "tags" , mod: 'boldital'} + ] + }] + }, { + lines: [{ + blocks: [ + {text: "label "}, + {text: "with" , mod: 'bold'}, + {text: " "}, + {text: "some" , mod: 'mono'}, + {text: " "} + ] + }, { + blocks: [ + {text: " "}, + {text: "multi ", mod: 'ital'}, + {text: "tags" , mod: 'boldital'} + ] + }, { + blocks: [{text: " and newlines"}] + }] + }]; + + + +/************************************************************** + * End Expected Results + **************************************************************/ + it('parses regular text labels', function (done) { var label = new Label({}, getOptions()); - checkProcessedLabels(label, text, expected); + + checkProcessedLabels(label, normal_text , normal_expected); + checkProcessedLabels(label, html_text , html_unchanged_expected); // html unchanged + checkProcessedLabels(label, markdown_text, markdown_unchanged_expected); // markdown unchanged done(); }); it('parses html labels', function (done) { - // NOTE: these are options at the node-level var options = getOptions(options); - - // Need to set these explicitly options.font.multi = true; // TODO: also test 'html', also test illegal value here - var localExpected = expected.slice(0,3); - Array.prototype.push.apply(localExpected, [ - { - lines: [{ - blocks: [ - {text: "label "}, - {text: "with" , mod: 'bold'}, - {text: " "}, - {text: "some" , mod: 'mono'}, - {text: " "}, - {text: "multi ", mod: 'ital'}, - {text: "tags" , mod: 'boldital'} - ] - }] - }, - { - lines: [{ - blocks: [ - {text: "label "}, - {text: "with" , mod: 'bold'}, - {text: " "}, - {text: "some" , mod: 'mono'}, - {text: " "} - ] - }, - { - blocks: [ - {text: " "}, - {text: "multi ", mod: 'ital'}, - {text: "tags" , mod: 'boldital'} - ] - }, - { - blocks: [{text: " and newlines"}] - }] - } - ]); - //console.log(JSON.stringify(localExpected, null, 2)); + var label = new Label({}, options); + + // normal should pass through unchanged + checkProcessedLabels(label, normal_text , normal_expected); // normal unchanged + checkProcessedLabels(label, html_text , multi_expected); + checkProcessedLabels(label, markdown_text, markdown_unchanged_expected); // markdown unchanged + + done(); + }); + it('parses markdown labels', function (done) { + var options = getOptions(options); + options.font.multi = 'markdown'; // TODO: also test 'md', also test illegal value here + var label = new Label({}, options); - checkProcessedLabels(label, text, localExpected); + + checkProcessedLabels(label, normal_text , normal_expected); // normal unchanged + checkProcessedLabels(label, html_text , html_unchanged_expected); // html unchanged + checkProcessedLabels(label, markdown_text, multi_expected); done(); }); From ace599a76ad9ee3e8b00407752bb6fc4add44417 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Sat, 17 Jun 2017 13:10:22 +0200 Subject: [PATCH 05/11] Further refactoring; made multi and regular handling more congruent --- .../modules/components/shared/Label.js | 216 +++++++++++------- test/Label.test.js | 41 ++-- 2 files changed, 157 insertions(+), 100 deletions(-) diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 139c966ed..5dfd6d99f 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -799,27 +799,18 @@ class Label { /** - * This explodes the passed text into lines and determines the width, height and number of lines. + * Initialize an accumulator object for label processing. + * + * This has been moved away from the label processing code for better undestanding upon reading. + * + * Note that lines is an Array instance with methods attached. * - * @param ctx - * @param selected - * @param hover - * @param {String} text the text to explode * @private */ - _processLabelText(ctx, selected, hover, text) { - if (text === undefined) { - return {width:0, height:0, lines: []}; - } - - let width = 0; - let height = 0; - let k = 0; - - // Note that lines is an array with methods attached + _createLabelAccumulator() { let lines = []; - lines.add = function(l, text, width, height, values, mod) { + lines.add = function(l, text, width, height, values, mod = 'normal') { if (this.length == l) { this[l] = { width: 0, height: 0, blocks: [] }; } @@ -836,6 +827,7 @@ class Label { }); } +/* lines.accumulate = function(l, width, height) { this[l].width += width; this[l].height = height > this[l].height ? height : this[l].height; @@ -843,9 +835,70 @@ class Label { lines.addAndAccumulate = function(l, text, width, height, values, mod = "normal") { this.add(l, text, width, height, values, mod); - this.accumulate(l, width, height); + //this.accumulate(l, width, height); + } +*/ + + return lines; + } + + + /** + * This explodes the passed text into lines and determines the width, height and number of lines. + * + * @param ctx + * @param selected + * @param hover + * @param {String} text the text to explode + * @private + */ + _processLabelText(ctx, selected, hover, text) { + if (text === undefined || text === "") { + return {width:0, height:0, lines: []}; + } + + let lines = this._createLabelAccumulator(); + + // + // Formatting values used *only* as input to the accumulator + // Also, width can be determined directly here + // + // Following take formatting values and measure var's out of the loops + let k = 0; + let self = this; + + let addText = function(k, text, mod = 'normal') { + if (text === undefined) return; + + // TODO: This can be done more efficiently by caching + let values = self.getFormattingValues(ctx, selected, hover, mod); + + // Note that an empty block will be added! + let width = 0; + if (text !== '') { + let measure = ctx.measureText(text); + width = measure.width; + } + + lines.add(k, text, width, values.height, values, mod); + } + + + // Add text in block to current line + let append = function(text, mod = 'normal') { + addText(k, text, mod); + } + + // Add text in block to current line and start a new line + let endLine = function(text, mod = 'normal') { + addText(k, text, mod); + k++; + } + + let overMaxWidth = function(text) { + let width = ctx.measureText(text).width; + return (width> self.fontOptions.maxWdt); } - // End def lines as an array with methods attached let nlLines = String(text).split('\n'); @@ -859,10 +912,7 @@ class Label { if (blocks === undefined) continue; if (blocks.length === 0) { - let values = this.getFormattingValues(ctx, selected, hover, "normal"); - lines.addAndAccumulate(k, "", 0, values.size, values); - height += lines[k].height; - k++; + endLine(""); continue; } @@ -871,123 +921,119 @@ class Label { for (let j = 0; j < blocks.length; j++) { let mod = blocks[j].mod; - let values = this.getFormattingValues(ctx, selected, hover, mod); let words = blocks[j].text.split(" "); - let atStart = true let text = ""; - let measure = { width: 0 }; - let lastMeasure; let lineWidth = 0; - let lineHeight = 0; let w = 0; while (w < words.length) { - let pre = atStart ? "" : " "; + let pre = (text === "") ? "" : " "; let newText = text + pre + words[w]; - lastMeasure = measure; - measure = ctx.measureText(newText); - - if ((lineWidth + measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { - lineHeight = (values.height > lineHeight) ? values.height : lineHeight; - lines.add(k, text, lastMeasure.width, values.height, values, mod); - lines.accumulate(k, lastMeasure.width, lineHeight); + let newWidth = ctx.measureText(newText).width; + if ((newWidth > this.fontOptions.maxWdt)) { + endLine(text, mod); text = ""; - atStart = true; - lineWidth = 0; - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; - k++; } else { text = newText; - if (w === words.length-1) { - lineHeight = (values.height > lineHeight) ? values.height : lineHeight; - lineWidth += measure.width; - lines.add(k, text, measure.width, values.height, values, mod); - lines.accumulate(k, measure.width, lineHeight); - if (j === blocks.length-1) { - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; - k++; - } - } w++; - atStart = false; } } + + // Add whatever is left after the word loop + if (text.length > 0) { + append(text, mod); + } } + + // Loop post-processing + newLine(); } else { // widthConstraint.maximum NOT defined for (let j = 0; j < blocks.length; j++) { let mod = blocks[j].mod; - let values = this.getFormattingValues(ctx, selected, hover, mod); - let measure = ctx.measureText(blocks[j].text); - - lines.addAndAccumulate(k, blocks[j].text, measure.width, values.height, values, mod); - width = lines[k].width > width ? lines[k].width : width; - if (blocks.length-1 === j) { - height += lines[k].height; - k++; - } + append(blocks[j].text, mod); } + + // Loop post-processing + newLine(); } } } else { // Single-font case - let addText = function(k, text, in_width, values) { - // Following var's in encompassing function scope. - // Hence the need to name param 'in_width'. - lines.addAndAccumulate(k, text, in_width, values.size, values); - width = lines[k].width > width ? lines[k].width : width; - height += lines[k].height; - } - - let values = this.getFormattingValues(ctx, selected, hover, "normal"); - if (this.fontOptions.maxWdt > 0) { // widthConstraint.maximum defined - for (let i = 0; i < lineCount; i++) { + // Handle words per line + let words = nlLines[i].split(" "); let text = ""; - let measure = { width: 0 }; + // Find the largest string smaller than the max width let w = 0; while (w < words.length) { let pre = (text === "") ? "" : " "; let newText = text + pre + words[w]; - let lastMeasure = measure; - measure = ctx.measureText(newText); - if ((measure.width > this.fontOptions.maxWdt) && (lastMeasure.width != 0)) { - addText(k, text, lastMeasure.width, values); + let newWidth = ctx.measureText(newText).width; + if (newWidth> this.fontOptions.maxWdt) { + // Current string too large; store what we got in a line and + // continue processing the next words for the new line. + newLine(text); text = ""; - k++; } else { text = newText; - if (w === words.length-1) { - addText(k, text, measure.width, values); - k++; - } w++; } } + + // Add whatever is left after the loop to a new line + if (text.length > 0) { + newLine(text); + } } } else { // widthConstraint.maximum NOT defined for (let i = 0; i < lineCount; i++) { let text = nlLines[i]; - let measure = ctx.measureText(text); - addText(k, text, measure.width, values); - k++; + newLine(text); } } } + // Determine the sizes of the lines + for (let k = 0; k < lines.length; k++) { + let line = lines[k]; + let width = 0; + let height = 0; + + for (let l = 0; l < line.blocks.length; l++) { + let block = line.blocks[l]; + if (block.width > width) { + width = block.width; + } + height += block.height; + } + + line.width = width; + line.height = height; + } + + // Determine the full label size + let width = 0; + let height = 0; + for (let k = 0; k < lines.length; k++) { + if (lines[k].width > width) { + width = lines[k].width; + } + height += lines[k].height; + } + + return {width, height, lines: lines}; } diff --git a/test/Label.test.js b/test/Label.test.js index fd365726f..47e3be7d5 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -56,8 +56,8 @@ describe('Network Label', function() { */ function checkBlocks(returned, expected) { let showBlocks = () => { - return 'returned: ' + JSON.stringify(returned, null, 2) + '\n' + - 'expected: ' + JSON.stringify(expected, null, 2); + return '\nreturned: ' + JSON.stringify(returned, null, 2) + '\n' + + 'expected: ' + JSON.stringify(expected, null, 2); } assert(returned.lines.length === expected.lines.length, 'Number of lines does not match, ' + showBlocks()); @@ -104,21 +104,18 @@ describe('Network Label', function() { var normal_text = [ "label text", "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined", - "label\nwith\nnewlines" + "label\nwith\nnewlines", + "One really long sentence that should go over widthConstraint.maximum if defined", ] var html_text = [ "label with some multi tags", - - // Note funky spaces around \n's in following - "label with some \n multi tags\n and newlines" + "label with some \n multi tags\n and newlines" // NB spaces around \n's ]; var markdown_text = [ "label *with* `some` _multi *tags*_", - - // Note funky spaces around \n's in following - "label *with* `some` \n _multi *tags*_\n and newlines" + "label *with* `some` \n _multi *tags*_\n and newlines" // NB spaces around \n's ]; @@ -140,8 +137,7 @@ describe('Network Label', function() { height: 14, }] }] - }, - { + }, { lines: [{ blocks: [{text: "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined"}] }] @@ -153,6 +149,10 @@ describe('Network Label', function() { }, { blocks: [{text: "newlines"}] }] + }, { + lines: [{ + blocks: [{text: "One really long sentence that should go over widthConstraint.maximum if defined"}] + }] }] @@ -224,7 +224,7 @@ describe('Network Label', function() { * End Expected Results **************************************************************/ - it('parses regular text labels', function (done) { + it('parses normal text labels', function (done) { var label = new Label({}, getOptions()); checkProcessedLabels(label, normal_text , normal_expected); @@ -241,8 +241,7 @@ describe('Network Label', function() { var label = new Label({}, options); - // normal should pass through unchanged - checkProcessedLabels(label, normal_text , normal_expected); // normal unchanged + checkProcessedLabels(label, normal_text , normal_expected); // normal as usual checkProcessedLabels(label, html_text , multi_expected); checkProcessedLabels(label, markdown_text, markdown_unchanged_expected); // markdown unchanged @@ -256,10 +255,22 @@ describe('Network Label', function() { var label = new Label({}, options); - checkProcessedLabels(label, normal_text , normal_expected); // normal unchanged + checkProcessedLabels(label, normal_text , normal_expected); // normal as usual checkProcessedLabels(label, html_text , html_unchanged_expected); // html unchanged checkProcessedLabels(label, markdown_text, multi_expected); done(); }); + + it('handles normal text labels with widthConstraint', function (done) { + var options = getOptions(options); + options.widthConstraint = { minimum: 100, maximum: 200}; + var label = new Label({}, options); + + checkProcessedLabels(label, normal_text , normal_expected); + checkProcessedLabels(label, html_text , html_unchanged_expected); // html unchanged + checkProcessedLabels(label, markdown_text, markdown_unchanged_expected); // markdown unchanged + + done(); + }); }); From 5717ff46fce11a126a697cc8cbcd327e922fa540 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Sun, 18 Jun 2017 11:52:03 +0200 Subject: [PATCH 06/11] Interim save, not there yet --- lib/network/modules/InteractionHandler.js | 9 +- .../modules/components/shared/Label.js | 358 +++++++++--------- test/Label.test.js | 100 ++++- 3 files changed, 285 insertions(+), 182 deletions(-) diff --git a/lib/network/modules/InteractionHandler.js b/lib/network/modules/InteractionHandler.js index 663608cdc..ff48e2d35 100644 --- a/lib/network/modules/InteractionHandler.js +++ b/lib/network/modules/InteractionHandler.js @@ -165,7 +165,14 @@ class InteractionHandler { onContext(event) { let pointer = this.getPointer({x:event.clientX, y:event.clientY}); - this.selectionHandler._generateClickEvent('oncontext', event, pointer); + +const hoveredElements = { + nodes: this.selectionHandler.getNodeAt(pointer), + edges: this.selectionHandler.getEdgeAt(pointer) + }; + + //this.selectionHandler._generateClickEvent('oncontext', event, pointer); + this.selectionHandler._generateClickEvent('oncontext', Object.assign({}, event, hoveredElements), pointer); } diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 5dfd6d99f..79f8d22a6 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -1,5 +1,96 @@ let util = require('../../../../util'); + +/** + * Internal helper class used for splitting a label text into lines. + * + * This has been moved away from the label processing code for better undestanding upon reading. + * + * @private + */ +class LabelAccumulator { + constructor() { + this.current = 0; + this.width = 0; + this.height = 0; + this.lines = []; + } + + + /** + * @private + */ + add(l, text, width, height, values, mod = 'normal') { + if (this.lines[l] === undefined) { + this.lines[l] = { + width : 0, + height: 0, + blocks: [] + }; + } + + this.lines[l].blocks.push({ + text, + font: values.font, + color: values.color, + width, height, + vadjust: values.vadjust, + mod, + strokeWidth: values.strokeWidth, + strokeColor: values.strokeColor + }); + } + + + /** + * Set the sizes for all lines and the whole thing. + */ + finalize() { + console.log(JSON.stringify(this.lines, null, 2)); + + // Determine the sizes of the lines + for (let k = 0; k < this.lines.length; k++) { + let line = this.lines[k]; + let width = 0; + let height = 0; + + for (let l = 0; l < line.blocks.length; l++) { + let block = line.blocks[l]; + if (block.width > width) { + width = block.width; + } + height += block.height; + } + + line.width = width; + line.height = height; + } + + // Determine the full label size + let width = 0; + let height = 0; + for (let k = 0; k < this.lines.length; k++) { + let line = this.lines[k]; + + if (line.width > width) { + width = line.width; + } + height += line.height; + } + + this.width = width; + this.height = height; + + // Return a simple hash object for further processing. + return { + width : this.width, + height: this.height, + lines : this.lines + } + } +} + + class Label { constructor(body, options, edgelabel = false) { this.body = body; @@ -796,51 +887,6 @@ class Label { differentState(selected, hover) { return ((selected !== this.fontOptions.selectedState) && (hover !== this.fontOptions.hoverState)); } - - - /** - * Initialize an accumulator object for label processing. - * - * This has been moved away from the label processing code for better undestanding upon reading. - * - * Note that lines is an Array instance with methods attached. - * - * @private - */ - _createLabelAccumulator() { - let lines = []; - - lines.add = function(l, text, width, height, values, mod = 'normal') { - if (this.length == l) { - this[l] = { width: 0, height: 0, blocks: [] }; - } - - this[l].blocks.push({ - text, - font: values.font, - color: values.color, - width, height, - vadjust: values.vadjust, - mod, - strokeWidth: values.strokeWidth, - strokeColor: values.strokeColor - }); - } - -/* - lines.accumulate = function(l, width, height) { - this[l].width += width; - this[l].height = height > this[l].height ? height : this[l].height; - } - - lines.addAndAccumulate = function(l, text, width, height, values, mod = "normal") { - this.add(l, text, width, height, values, mod); - //this.accumulate(l, width, height); - } -*/ - - return lines; - } /** @@ -853,188 +899,160 @@ class Label { * @private */ _processLabelText(ctx, selected, hover, text) { + let lines = new LabelAccumulator(); + if (text === undefined || text === "") { - return {width:0, height:0, lines: []}; + return lines.finalize(); } - let lines = this._createLabelAccumulator(); - - // - // Formatting values used *only* as input to the accumulator - // Also, width can be determined directly here - // - // Following take formatting values and measure var's out of the loops - let k = 0; let self = this; - let addText = function(k, text, mod = 'normal') { - if (text === undefined) return; - // TODO: This can be done more efficiently by caching - let values = self.getFormattingValues(ctx, selected, hover, mod); + /** + * Formatting values used *only* as input to the accumulator. + * Also, width can be determined directly here. + * + * Following take formatting values and measure var's out of the loops + */ + let addText = function(k, text, mod = 'normal') { + if (text === undefined) return; - // Note that an empty block will be added! - let width = 0; - if (text !== '') { - let measure = ctx.measureText(text); - width = measure.width; - } + // TODO: This can be done more efficiently by caching + let values = self.getFormattingValues(ctx, selected, hover, mod); - lines.add(k, text, width, values.height, values, mod); - } + // Note that an empty block will be added! + let width = 0; + if (text !== '') { + // NOTE: The following may actually be *incorrect* for the mod fonts! + // This returns the size with a regular font, bold etc. may + // have different sizes. + let measure = ctx.measureText(text); + width = measure.width; + } + lines.add(k, text, width, values.height, values, mod); + } - // Add text in block to current line - let append = function(text, mod = 'normal') { - addText(k, text, mod); - } - // Add text in block to current line and start a new line - let endLine = function(text, mod = 'normal') { - addText(k, text, mod); - k++; + /** + * Add text in block to current line + */ + let append = function(text, mod = 'normal') { + addText(lines.current, text, mod); + } + + + /** + * Add text in block to current line and start a new line + */ + let newLine = function(text, mod = 'normal') { + addText(lines.current, text, mod); + lines.current++; } + let overMaxWidth = function(text) { let width = ctx.measureText(text).width; return (width> self.fontOptions.maxWdt); } + /** + * Determine the longest string which would still fit in the + * current max width. + * + * @param {Array} words Array of strings signifying a text lines + * @return index of first item in string making string go over max + */ + let getLongestFit = function(words) { + let text = ''; + let w = 0; + + while (w < words.length) { + let pre = (text === '') ? '' : ' '; + let newText = text + pre + words[w]; + + if (overMaxWidth(newText)) { + break; + } else { + text = newText; + w++; + } + } + + return w; + } + + + let splitStringIntoLines = function(str, mod = 'normal') { + let words = str.split(" "); + + while (words.length > 0) { + let w = getLongestFit(words); + + // Special case: the first word may already + // be larger than the max width. + // In this case, just force using it; it will + // be just that one word on a line + if (w === 0) w = 1; + + let text = words.slice(0, w).join(" "); + newLine(text, mod); + words = words.slice(w); + } + } + + let nlLines = String(text).split('\n'); let lineCount = nlLines.length; if (this.elementOptions.font.multi) { // Multi-font case: styling tags active - for (let i = 0; i < lineCount; i++) { let blocks = this.splitBlocks(nlLines[i], this.elementOptions.font.multi); if (blocks === undefined) continue; if (blocks.length === 0) { - endLine(""); + newLine(""); continue; } if (this.fontOptions.maxWdt > 0) { // widthConstraint.maximum defined - + //console.log('Running widthConstraint multi, max: ' + this.fontOptions.maxWdt); for (let j = 0; j < blocks.length; j++) { - let mod = blocks[j].mod; - let words = blocks[j].text.split(" "); - let text = ""; - let lineWidth = 0; - - let w = 0; - while (w < words.length) { - let pre = (text === "") ? "" : " "; - let newText = text + pre + words[w]; - - let newWidth = ctx.measureText(newText).width; - if ((newWidth > this.fontOptions.maxWdt)) { - endLine(text, mod); - text = ""; - } else { - text = newText; - w++; - } - } - - // Add whatever is left after the word loop - if (text.length > 0) { - append(text, mod); - } + let mod = blocks[j].mod; + let text = blocks[j].text; + splitStringIntoLines(text, mod); } - - // Loop post-processing - newLine(); } else { // widthConstraint.maximum NOT defined - for (let j = 0; j < blocks.length; j++) { - let mod = blocks[j].mod; - append(blocks[j].text, mod); + let mod = blocks[j].mod; + let text = blocks[j].text; + append(text, mod); } - // Loop post-processing newLine(); } } } else { // Single-font case - if (this.fontOptions.maxWdt > 0) { // widthConstraint.maximum defined - + // console.log('Running widthConstraint normal, max: ' + this.fontOptions.maxWdt); for (let i = 0; i < lineCount; i++) { - // Handle words per line - - let words = nlLines[i].split(" "); - let text = ""; - - // Find the largest string smaller than the max width - let w = 0; - while (w < words.length) { - let pre = (text === "") ? "" : " "; - let newText = text + pre + words[w]; - - let newWidth = ctx.measureText(newText).width; - if (newWidth> this.fontOptions.maxWdt) { - // Current string too large; store what we got in a line and - // continue processing the next words for the new line. - newLine(text); - text = ""; - } else { - text = newText; - w++; - } - } - - // Add whatever is left after the loop to a new line - if (text.length > 0) { - newLine(text); - } + splitStringIntoLines(nlLines[i]); } } else { // widthConstraint.maximum NOT defined - for (let i = 0; i < lineCount; i++) { - let text = nlLines[i]; - newLine(text); + newLine(nlLines[i]); } } } - - // Determine the sizes of the lines - for (let k = 0; k < lines.length; k++) { - let line = lines[k]; - let width = 0; - let height = 0; - - for (let l = 0; l < line.blocks.length; l++) { - let block = line.blocks[l]; - if (block.width > width) { - width = block.width; - } - height += block.height; - } - - line.width = width; - line.height = height; - } - - // Determine the full label size - let width = 0; - let height = 0; - for (let k = 0; k < lines.length; k++) { - if (lines[k].width > width) { - width = lines[k].width; - } - height += lines[k].height; - } - - return {width, height, lines: lines}; + return lines.finalize(); } diff --git a/test/Label.test.js b/test/Label.test.js index 47e3be7d5..c4028f84e 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -32,8 +32,7 @@ describe('Network Label', function() { * * NOTE: these are options at the node-level */ - function getOptions() { - var options = {}; + function getOptions(options = {}) { var body = { functions: {}, emitter: { @@ -60,7 +59,7 @@ describe('Network Label', function() { 'expected: ' + JSON.stringify(expected, null, 2); } - assert(returned.lines.length === expected.lines.length, 'Number of lines does not match, ' + showBlocks()); + assert.equal(expected.lines.length, returned.lines.length, 'Number of lines does not match, ' + showBlocks()); for (let i = 0; i < returned.lines.length; ++i) { let retLine = returned.lines[i]; @@ -153,7 +152,22 @@ describe('Network Label', function() { lines: [{ blocks: [{text: "One really long sentence that should go over widthConstraint.maximum if defined"}] }] - }] + }]; + + + // First three same as normal_expected + var normal_widthConstraint_expected = normal_expected.slice(0,3); + Array.prototype.push.apply(normal_widthConstraint_expected, [{ + lines: [{ + blocks: [{text: "One really long sentence"}] + }, { + blocks: [{text: "that should go over"}] + }, { + blocks: [{text: "widthConstraint.maximum"}] + }, { + blocks: [{text: "if defined"}] + }] + }]); var html_unchanged_expected = [{ @@ -168,7 +182,27 @@ describe('Network Label', function() { }, { blocks: [{text: " and newlines"}] }] - }] + }]; + + var html_widthConstraint_unchanged = [{ + lines: [{ + blocks: [{text: "label with"}] + }, { + blocks: [{text: "some"}] + }, { + blocks: [{text: "multi tags"}] + }] + }, { + lines: [{ + blocks: [{text: "label with"}] + }, { + blocks: [{text: "some "}] + }, { + blocks: [{text: " multi tags"}] + }, { + blocks: [{text: " and newlines"}] + }] + }]; var markdown_unchanged_expected = [{ @@ -183,7 +217,24 @@ describe('Network Label', function() { }, { blocks: [{text: " and newlines"}] }] - }] + }]; + + + var markdown_widthConstraint_expected = [{ + lines: [{ + blocks: [{text: "label *with* `some`"}] + }, { + blocks: [{text: "_multi *tags*_"}] + }] + }, { + lines: [{ + blocks: [{text: "label *with* `some` "}] + }, { + blocks: [{text: " _multi *tags*_"}] + }, { + blocks: [{text: " and newlines"}] + }] + }]; var multi_expected = [{ @@ -262,14 +313,41 @@ describe('Network Label', function() { done(); }); - it('handles normal text labels with widthConstraint', function (done) { + + it('handles normal text with widthConstraint.maximum', function (done) { var options = getOptions(options); - options.widthConstraint = { minimum: 100, maximum: 200}; + + // + // What the user would set: + // + // options.widthConstraint = { minimum: 100, maximum: 200}; + // + // No sense in adding minWdt, not used when splitting labels into lines + // + // This comment also applies to the usage of maxWdt in the test cases below + // + options.font.maxWdt = 300; + var label = new Label({}, options); - checkProcessedLabels(label, normal_text , normal_expected); - checkProcessedLabels(label, html_text , html_unchanged_expected); // html unchanged - checkProcessedLabels(label, markdown_text, markdown_unchanged_expected); // markdown unchanged + checkProcessedLabels(label, normal_text , normal_widthConstraint_expected); + checkProcessedLabels(label, html_text , html_widthConstraint_unchanged); // html unchanged + checkProcessedLabels(label, markdown_text, markdown_widthConstraint_expected); // markdown unchanged + + done(); + }); + + + it('handles html tags with widthConstraint.maximum', function (done) { + var options = getOptions(options); + options.font.multi = true; // TODO: also test 'html', also test illegal value here + options.font.maxWdt = 300; + + var label = new Label({}, options); + + checkProcessedLabels(label, normal_text , normal_widthConstraint_expected); + checkProcessedLabels(label, html_text , multi_expected); + checkProcessedLabels(label, markdown_text, markdown_widthConstraint_expected); // markdown unchanged done(); }); From 66b2f5081420dc4b85cc849f56096b3f9fdf0028 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Sun, 18 Jun 2017 13:08:43 +0200 Subject: [PATCH 07/11] Unit tests done, first working version --- .../modules/components/shared/Label.js | 139 +++++++++++------- test/Label.test.js | 33 ++++- 2 files changed, 118 insertions(+), 54 deletions(-) diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 79f8d22a6..a8839e517 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -9,7 +9,8 @@ let util = require('../../../../util'); * @private */ class LabelAccumulator { - constructor() { + constructor(measureText) { + this.measureText = measureText; // callback to determine text dimensions, using the parent label settings. this.current = 0; this.width = 0; this.height = 0; @@ -39,6 +40,54 @@ class LabelAccumulator { strokeWidth: values.strokeWidth, strokeColor: values.strokeColor }); + + this.lines[l].width += width; + } + + + /** + * Returns the width in pixels of the current line. + */ + curWidth() { + let line = this.lines[this.current]; + if (line === undefined) return 0; + + return line.width; + } + + + /** + * Formatting values used *only* as input to the accumulator. + * Also, width can be determined directly here. + * + * This method takes formatting values and measure var's out of the loops. + * + * @private + * + */ + addText(k, text, mod = 'normal') { + if (text === undefined) return; + + // Note that an empty block (width 0) will be added! + let result = this.measureText(text, mod); + this.add(k, text, result.width, result.values.height, result.values, mod); + } + + + /** + * Add text in block to current line + */ + append(text, mod = 'normal') { + this.addText(this.current, text, mod); + } + + + /** + * Add text in block to current line and start a new line + */ + newLine(text, mod = 'normal') { + this.addText(this.current, text, mod); + this.current++; } @@ -46,7 +95,7 @@ class LabelAccumulator { * Set the sizes for all lines and the whole thing. */ finalize() { - console.log(JSON.stringify(this.lines, null, 2)); + // console.log(JSON.stringify(this.lines, null, 2)); // Determine the sizes of the lines for (let k = 0; k < this.lines.length; k++) { @@ -899,61 +948,45 @@ class Label { * @private */ _processLabelText(ctx, selected, hover, text) { - let lines = new LabelAccumulator(); - - if (text === undefined || text === "") { - return lines.finalize(); - } - let self = this; /** - * Formatting values used *only* as input to the accumulator. - * Also, width can be determined directly here. + * Callback to determine text width; passed to LabelAccumulator instance * - * Following take formatting values and measure var's out of the loops + * @param {String} text string to determine width of + * @param {String} mod font type to use for this text + * @return {Object} { width, values} width in pixels and return value of measuring */ - let addText = function(k, text, mod = 'normal') { - if (text === undefined) return; - - // TODO: This can be done more efficiently by caching - let values = self.getFormattingValues(ctx, selected, hover, mod); - - // Note that an empty block will be added! - let width = 0; - if (text !== '') { - // NOTE: The following may actually be *incorrect* for the mod fonts! - // This returns the size with a regular font, bold etc. may - // have different sizes. - let measure = ctx.measureText(text); - width = measure.width; - } - - lines.add(k, text, width, values.height, values, mod); - } + let textWidth = function(text, mod) { + if (text === undefined) return 0; + + // TODO: This can be done more efficiently by caching + let values = self.getFormattingValues(ctx, selected, hover, mod); + + let width = 0; + if (text !== '') { + // NOTE: The following may actually be *incorrect* for the mod fonts! + // This returns the size with a regular font, bold etc. may + // have different sizes. + let measure = ctx.measureText(text); + width = measure.width; + } + return { width, values: values}; + }; - /** - * Add text in block to current line - */ - let append = function(text, mod = 'normal') { - addText(lines.current, text, mod); - } + let lines = new LabelAccumulator(textWidth); - /** - * Add text in block to current line and start a new line - */ - let newLine = function(text, mod = 'normal') { - addText(lines.current, text, mod); - lines.current++; + if (text === undefined || text === "") { + return lines.finalize(); } let overMaxWidth = function(text) { let width = ctx.measureText(text).width; - return (width> self.fontOptions.maxWdt); + return (lines.curWidth() + width > self.fontOptions.maxWdt); } @@ -984,7 +1017,7 @@ class Label { } - let splitStringIntoLines = function(str, mod = 'normal') { + let splitStringIntoLines = function(str, mod = 'normal', appendLast = false) { let words = str.split(" "); while (words.length > 0) { @@ -997,7 +1030,13 @@ class Label { if (w === 0) w = 1; let text = words.slice(0, w).join(" "); - newLine(text, mod); + + if (w == words.length && appendLast) { + lines.append(text, mod); + } else { + lines.newLine(text, mod); + } + words = words.slice(w); } } @@ -1013,7 +1052,7 @@ class Label { if (blocks === undefined) continue; if (blocks.length === 0) { - newLine(""); + lines.newLine(""); continue; } @@ -1023,18 +1062,18 @@ class Label { for (let j = 0; j < blocks.length; j++) { let mod = blocks[j].mod; let text = blocks[j].text; - splitStringIntoLines(text, mod); + splitStringIntoLines(text, mod, true); } } else { // widthConstraint.maximum NOT defined for (let j = 0; j < blocks.length; j++) { let mod = blocks[j].mod; let text = blocks[j].text; - append(text, mod); + lines.append(text, mod); } - - newLine(); } + + lines.newLine(); } } else { // Single-font case @@ -1047,7 +1086,7 @@ class Label { } else { // widthConstraint.maximum NOT defined for (let i = 0; i < lineCount; i++) { - newLine(nlLines[i]); + lines.newLine(nlLines[i]); } } } diff --git a/test/Label.test.js b/test/Label.test.js index c4028f84e..fd2d4036f 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -1,3 +1,11 @@ +/** + * TODO - add tests for: + * ==== + * + * - pathological cases of spaces (and other whitespace!) + * - html unclosed or unopened tags + * - html tag combinations with no font defined (e.g. bold within mono) + */ var assert = require('assert') var Label = require('../lib/network/modules/components/shared/Label').default; var NodesHandler = require('../lib/network/modules/NodesHandler').default; @@ -123,6 +131,7 @@ describe('Network Label', function() { * Expected Results **************************************************************/ + var normal_expected = [{ // In first item, width/height kept in for reference width: 120, @@ -149,14 +158,15 @@ describe('Network Label', function() { blocks: [{text: "newlines"}] }] }, { + // Changes width max width set lines: [{ blocks: [{text: "One really long sentence that should go over widthConstraint.maximum if defined"}] }] }]; + const indexWidthConstrained = normal_expected.length - 1; // index of first item that will be different with max width set - // First three same as normal_expected - var normal_widthConstraint_expected = normal_expected.slice(0,3); + var normal_widthConstraint_expected = normal_expected.slice(0, indexWidthConstrained); Array.prototype.push.apply(normal_widthConstraint_expected, [{ lines: [{ blocks: [{text: "One really long sentence"}] @@ -340,14 +350,29 @@ describe('Network Label', function() { it('handles html tags with widthConstraint.maximum', function (done) { var options = getOptions(options); - options.font.multi = true; // TODO: also test 'html', also test illegal value here + options.font.multi = true; options.font.maxWdt = 300; var label = new Label({}, options); checkProcessedLabels(label, normal_text , normal_widthConstraint_expected); checkProcessedLabels(label, html_text , multi_expected); - checkProcessedLabels(label, markdown_text, markdown_widthConstraint_expected); // markdown unchanged + checkProcessedLabels(label, markdown_text, markdown_widthConstraint_expected); + + done(); + }); + + + it('handles markdown tags with widthConstraint.maximum', function (done) { + var options = getOptions(options); + options.font.multi = 'markdown'; + options.font.maxWdt = 300; + + var label = new Label({}, options); + + checkProcessedLabels(label, normal_text , normal_widthConstraint_expected); + checkProcessedLabels(label, html_text , html_widthConstraint_unchanged); + checkProcessedLabels(label, markdown_text, multi_expected); done(); }); From afba9f65b0ad4c32ee4d541bcd6a0a5db23fb56b Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Sun, 18 Jun 2017 13:13:43 +0200 Subject: [PATCH 08/11] Added test case with two big words --- test/Label.test.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/Label.test.js b/test/Label.test.js index fd2d4036f..a2452c322 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -113,6 +113,7 @@ describe('Network Label', function() { "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined", "label\nwith\nnewlines", "One really long sentence that should go over widthConstraint.maximum if defined", + "Reallyonehellalargelabel withtwobigwordsgoingovermax" ] var html_text = [ @@ -158,13 +159,17 @@ describe('Network Label', function() { blocks: [{text: "newlines"}] }] }, { - // Changes width max width set + // From here onwared, changes width max width set lines: [{ blocks: [{text: "One really long sentence that should go over widthConstraint.maximum if defined"}] }] + }, { + lines: [{ + blocks: [{text: "Reallyonehellalargelabel withtwobigwordsgoingovermax"}] + }] }]; - const indexWidthConstrained = normal_expected.length - 1; // index of first item that will be different with max width set + const indexWidthConstrained = normal_expected.length - 2; // index of first item that will be different with max width set var normal_widthConstraint_expected = normal_expected.slice(0, indexWidthConstrained); Array.prototype.push.apply(normal_widthConstraint_expected, [{ @@ -177,6 +182,12 @@ describe('Network Label', function() { }, { blocks: [{text: "if defined"}] }] + }, { + lines: [{ + blocks: [{text: "Reallyonehellalargelabel"}] + }, { + blocks: [{text: "withtwobigwordsgoingovermax"}] + }] }]); From c0a6b5d407fd50a2895f4666550943191bc6bf3a Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Mon, 19 Jun 2017 08:15:17 +0200 Subject: [PATCH 09/11] Code cleanup --- .../modules/components/shared/Label.js | 71 ++++++++----------- test/Label.test.js | 7 +- 2 files changed, 32 insertions(+), 46 deletions(-) diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index a8839e517..4a847ecdb 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -19,9 +19,16 @@ class LabelAccumulator { /** + * Append given text to the given line. + * + * @param {number} l index of line to add to + * @param {string} text string to append to line + * @param {boolean} mod id of multi-font to use, default 'normal' * @private */ - add(l, text, width, height, values, mod = 'normal') { + _add(l, text, mod = 'normal') { + if (text === undefined || text === "") return; + if (this.lines[l] === undefined) { this.lines[l] = { width : 0, @@ -30,18 +37,18 @@ class LabelAccumulator { }; } - this.lines[l].blocks.push({ - text, - font: values.font, - color: values.color, - width, height, - vadjust: values.vadjust, - mod, - strokeWidth: values.strokeWidth, - strokeColor: values.strokeColor - }); - - this.lines[l].width += width; + // Determine width and get the font properties + let result = this.measureText(text, mod); + let block = Object.assign({}, result.values); + block.text = text; + block.width = result.width; + block.mod = mod; + + this.lines[l].blocks.push(block); + + // Update the line width. We need this for + // determining if a string goes over max width + this.lines[l].width += result.width; } @@ -56,29 +63,11 @@ class LabelAccumulator { } - /** - * Formatting values used *only* as input to the accumulator. - * Also, width can be determined directly here. - * - * This method takes formatting values and measure var's out of the loops. - * - * @private - * - */ - addText(k, text, mod = 'normal') { - if (text === undefined) return; - - // Note that an empty block (width 0) will be added! - let result = this.measureText(text, mod); - this.add(k, text, result.width, result.values.height, result.values, mod); - } - - /** * Add text in block to current line */ append(text, mod = 'normal') { - this.addText(this.current, text, mod); + this._add(this.current, text, mod); } @@ -86,7 +75,7 @@ class LabelAccumulator { * Add text in block to current line and start a new line */ newLine(text, mod = 'normal') { - this.addText(this.current, text, mod); + this._add(this.current, text, mod); this.current++; } @@ -97,21 +86,17 @@ class LabelAccumulator { finalize() { // console.log(JSON.stringify(this.lines, null, 2)); - // Determine the sizes of the lines + // Determine the heights of the lines + // Note that width has already been set for (let k = 0; k < this.lines.length; k++) { let line = this.lines[k]; - let width = 0; let height = 0; for (let l = 0; l < line.blocks.length; l++) { let block = line.blocks[l]; - if (block.width > width) { - width = block.width; - } height += block.height; } - line.width = width; line.height = height; } @@ -956,12 +941,12 @@ class Label { * * @param {String} text string to determine width of * @param {String} mod font type to use for this text - * @return {Object} { width, values} width in pixels and return value of measuring + * @return {Object} { width, values} width in pixels and font attributes */ let textWidth = function(text, mod) { if (text === undefined) return 0; - // TODO: This can be done more efficiently by caching + // TODO: This can be done more efficiently with caching let values = self.getFormattingValues(ctx, selected, hover, mod); let width = 0; @@ -973,7 +958,7 @@ class Label { width = measure.width; } - return { width, values: values}; + return {width, values: values}; }; @@ -1026,7 +1011,7 @@ class Label { // Special case: the first word may already // be larger than the max width. // In this case, just force using it; it will - // be just that one word on a line + // be just that one word on a line. if (w === 0) w = 1; let text = words.slice(0, w).join(" "); diff --git a/test/Label.test.js b/test/Label.test.js index a2452c322..39256261d 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -2,6 +2,7 @@ * TODO - add tests for: * ==== * + * - !!! good test case with the tags for max width * - pathological cases of spaces (and other whitespace!) * - html unclosed or unopened tags * - html tag combinations with no font defined (e.g. bold within mono) @@ -81,8 +82,8 @@ describe('Network Label', function() { assert(retBlock.text === expBlock.text, 'Text does not match, ' + showBlocks()); assert(retBlock.mod !== undefined); - if (retBlock.mod === 'normal') { - assert(expBlock.mod === undefined || expBlock.mod === 'normal', + if (retBlock.mod === 'normal' || retBlock.mod === '') { + assert(expBlock.mod === undefined || expBlock.mod === 'normal' || expBlock === '', 'No mod field expected in returned, ' + showBlocks()); } else { assert(retBlock.mod === expBlock.mod, 'Mod fields do not match, line: ' + i + ', block: ' + j + @@ -159,7 +160,7 @@ describe('Network Label', function() { blocks: [{text: "newlines"}] }] }, { - // From here onwared, changes width max width set + // From here onward, changes width max width set lines: [{ blocks: [{text: "One really long sentence that should go over widthConstraint.maximum if defined"}] }] From 764d44f23005b316336569ec1d72c772f46c88d4 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Mon, 19 Jun 2017 08:45:01 +0200 Subject: [PATCH 10/11] Break up huge words into lines. --- .../modules/components/shared/Label.js | 58 +++++++++++++------ test/Label.test.js | 32 ++++++---- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 4a847ecdb..da037b9df 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -976,7 +976,7 @@ class Label { /** - * Determine the longest string which would still fit in the + * Determine the longest part of the sentence which still fits in the * current max width. * * @param {Array} words Array of strings signifying a text lines @@ -990,12 +990,27 @@ class Label { let pre = (text === '') ? '' : ' '; let newText = text + pre + words[w]; - if (overMaxWidth(newText)) { - break; - } else { - text = newText; - w++; - } + if (overMaxWidth(newText)) break; + text = newText; + w++; + } + + return w; + } + + /** + * Determine the longest part of th string which still fits in the + * current max width. + * + * @param {Array} words Array of strings signifying a text lines + * @return index of first item in string making string go over max + */ + let getLongestFitWord = function(word) { + let w = 0; + + while (w < word.length) { + if (overMaxWidth(word.slice(0,w))) break; + w++; } return w; @@ -1008,21 +1023,28 @@ class Label { while (words.length > 0) { let w = getLongestFit(words); - // Special case: the first word may already - // be larger than the max width. - // In this case, just force using it; it will - // be just that one word on a line. - if (w === 0) w = 1; + if (w === 0) { + // Special case: the first word may already + // be larger than the max width. + let word = words[0]; - let text = words.slice(0, w).join(" "); + // Break the word to the largest part that fits the line + let x = getLongestFitWord(word); + lines.newLine(word.slice(0, x), mod); - if (w == words.length && appendLast) { - lines.append(text, mod); + // Adjust the word, so that the rest will be done next iteration + words[0] = word.slice(x); } else { - lines.newLine(text, mod); - } + let text = words.slice(0, w).join(" "); - words = words.slice(w); + if (w == words.length && appendLast) { + lines.append(text, mod); + } else { + lines.newLine(text, mod); + } + + words = words.slice(w); + } } } diff --git a/test/Label.test.js b/test/Label.test.js index 39256261d..97b504af6 100644 --- a/test/Label.test.js +++ b/test/Label.test.js @@ -111,10 +111,10 @@ describe('Network Label', function() { var normal_text = [ "label text", - "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined", "label\nwith\nnewlines", + "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined", "One really long sentence that should go over widthConstraint.maximum if defined", - "Reallyonehellalargelabel withtwobigwordsgoingovermax" + "Reallyoneenormouslylargelabel withtwobigwordsgoingoverwayovermax" ] var html_text = [ @@ -147,10 +147,6 @@ describe('Network Label', function() { height: 14, }] }] - }, { - lines: [{ - blocks: [{text: "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined"}] - }] }, { lines: [{ blocks: [{text: "label"}] @@ -161,19 +157,31 @@ describe('Network Label', function() { }] }, { // From here onward, changes width max width set + lines: [{ + blocks: [{text: "OnereallylongwordthatshouldgooverwidthConstraint.maximumifdefined"}] + }] + }, { lines: [{ blocks: [{text: "One really long sentence that should go over widthConstraint.maximum if defined"}] }] }, { lines: [{ - blocks: [{text: "Reallyonehellalargelabel withtwobigwordsgoingovermax"}] + blocks: [{text: "Reallyoneenormouslylargelabel withtwobigwordsgoingoverwayovermax"}] }] }]; - const indexWidthConstrained = normal_expected.length - 2; // index of first item that will be different with max width set + const indexWidthConstrained = 2; // index of first item that will be different with max width set var normal_widthConstraint_expected = normal_expected.slice(0, indexWidthConstrained); Array.prototype.push.apply(normal_widthConstraint_expected, [{ + lines: [{ + blocks: [{text: "Onereallylongwordthatshoul"}] + }, { + blocks: [{text: "dgooverwidthConstraint.max"}] + }, { + blocks: [{text: "imumifdefined"}] + }] + }, { lines: [{ blocks: [{text: "One really long sentence"}] }, { @@ -185,9 +193,13 @@ describe('Network Label', function() { }] }, { lines: [{ - blocks: [{text: "Reallyonehellalargelabel"}] + blocks: [{text: "Reallyoneenormouslylargela"}] + }, { + blocks: [{text: "bel"}] + }, { + blocks: [{text: "withtwobigwordsgoingoverwa"}] }, { - blocks: [{text: "withtwobigwordsgoingovermax"}] + blocks: [{text: "yovermax"}] }] }]); From 7e52abe237761aff588a41e9c4a62cb21a38eed7 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Tue, 20 Jun 2017 09:34:44 +0200 Subject: [PATCH 11/11] Restore unrelated code change --- lib/network/modules/InteractionHandler.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/network/modules/InteractionHandler.js b/lib/network/modules/InteractionHandler.js index ff48e2d35..663608cdc 100644 --- a/lib/network/modules/InteractionHandler.js +++ b/lib/network/modules/InteractionHandler.js @@ -165,14 +165,7 @@ class InteractionHandler { onContext(event) { let pointer = this.getPointer({x:event.clientX, y:event.clientY}); - -const hoveredElements = { - nodes: this.selectionHandler.getNodeAt(pointer), - edges: this.selectionHandler.getEdgeAt(pointer) - }; - - //this.selectionHandler._generateClickEvent('oncontext', event, pointer); - this.selectionHandler._generateClickEvent('oncontext', Object.assign({}, event, hoveredElements), pointer); + this.selectionHandler._generateClickEvent('oncontext', event, pointer); }