mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-17 11:14:12 +01:00
Merge branch 'develop' into feature/1483_long_messages
This commit is contained in:
113
src/utils.js
113
src/utils.js
@@ -106,8 +106,8 @@ export const detectInit = function(text) {
|
|||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param {string} text The text defining the graph
|
* @param {string} text The text defining the graph
|
||||||
* @param {string|RegExp} type The directive to return (default: null
|
* @param {string|RegExp} type The directive to return (default: null)
|
||||||
* @returns {object | Array} An object or Array representing the directive(s): { type: string, args: object|null } matchd by the input type
|
* @returns {object | Array} An object or Array representing the directive(s): { type: string, args: object|null } matched by the input type
|
||||||
* if a single directive was found, that directive object will be returned.
|
* if a single directive was found, that directive object will be returned.
|
||||||
*/
|
*/
|
||||||
export const detectDirective = function(text, type = null) {
|
export const detectDirective = function(text, type = null) {
|
||||||
@@ -221,6 +221,20 @@ export const detectType = function(text) {
|
|||||||
return 'flowchart';
|
return 'flowchart';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const memoize = (fn, resolver) => {
|
||||||
|
let cache = {};
|
||||||
|
return (...args) => {
|
||||||
|
let n = resolver ? resolver.apply(this, args) : args[0];
|
||||||
|
if (n in cache) {
|
||||||
|
return cache[n];
|
||||||
|
} else {
|
||||||
|
let result = fn(...args);
|
||||||
|
cache[n] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function isSubstringInArray
|
* @function isSubstringInArray
|
||||||
* Detects whether a substring in present in a given array
|
* Detects whether a substring in present in a given array
|
||||||
@@ -256,13 +270,13 @@ export const formatUrl = (linkStr, config) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const runFunc = (functionName, ...params) => {
|
export const runFunc = (functionName, ...params) => {
|
||||||
var arrPaths = functionName.split('.');
|
const arrPaths = functionName.split('.');
|
||||||
|
|
||||||
var len = arrPaths.length - 1;
|
const len = arrPaths.length - 1;
|
||||||
var fnName = arrPaths[len];
|
const fnName = arrPaths[len];
|
||||||
|
|
||||||
var obj = window;
|
let obj = window;
|
||||||
for (var i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
obj = obj[arrPaths[i]];
|
obj = obj[arrPaths[i]];
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
}
|
}
|
||||||
@@ -283,10 +297,8 @@ const traverseEdge = points => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Traverse half of total distance along points
|
// Traverse half of total distance along points
|
||||||
const distanceToLabel = totalDistance / 2;
|
let remainingDistance = totalDistance / 2;
|
||||||
|
let center = undefined;
|
||||||
let remainingDistance = distanceToLabel;
|
|
||||||
let center;
|
|
||||||
prevPoint = undefined;
|
prevPoint = undefined;
|
||||||
points.forEach(point => {
|
points.forEach(point => {
|
||||||
if (prevPoint && !center) {
|
if (prevPoint && !center) {
|
||||||
@@ -313,8 +325,7 @@ const traverseEdge = points => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const calcLabelPosition = points => {
|
const calcLabelPosition = points => {
|
||||||
const p = traverseEdge(points);
|
return traverseEdge(points);
|
||||||
return p;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
|
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
|
||||||
@@ -332,7 +343,7 @@ const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition)
|
|||||||
const distanceToCardinalityPoint = 25;
|
const distanceToCardinalityPoint = 25;
|
||||||
|
|
||||||
let remainingDistance = distanceToCardinalityPoint;
|
let remainingDistance = distanceToCardinalityPoint;
|
||||||
let center;
|
let center = { x: 0, y: 0 };
|
||||||
prevPoint = undefined;
|
prevPoint = undefined;
|
||||||
points.forEach(point => {
|
points.forEach(point => {
|
||||||
if (prevPoint && !center) {
|
if (prevPoint && !center) {
|
||||||
@@ -498,11 +509,8 @@ export const drawSimpleText = function(elem, textData) {
|
|||||||
return textElem;
|
return textElem;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const wrapLabel = (label, maxWidth, config) => {
|
export const wrapLabel = memoize(
|
||||||
if (!wrapLabel.cache) {
|
(label, maxWidth, config) => {
|
||||||
// until memoize PR
|
|
||||||
wrapLabel.cache = {};
|
|
||||||
}
|
|
||||||
if (!label) {
|
if (!label) {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
@@ -510,10 +518,6 @@ export const wrapLabel = (label, maxWidth, config) => {
|
|||||||
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', joinWith: '<br/>' },
|
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', joinWith: '<br/>' },
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
const cacheKey = `${label}-${maxWidth}-${JSON.stringify(config)}`;
|
|
||||||
if (wrapLabel.cache[cacheKey]) {
|
|
||||||
return wrapLabel.cache[cacheKey];
|
|
||||||
}
|
|
||||||
if (common.lineBreakRegex.test(label)) {
|
if (common.lineBreakRegex.test(label)) {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
@@ -539,20 +543,18 @@ export const wrapLabel = (label, maxWidth, config) => {
|
|||||||
completedLines.push(nextLine);
|
completedLines.push(nextLine);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const result = completedLines.filter(line => line !== '').join(config.joinWith);
|
return completedLines.filter(line => line !== '').join(config.joinWith);
|
||||||
wrapLabel.cache[cacheKey] = result;
|
},
|
||||||
return result;
|
(label, maxWidth, config) =>
|
||||||
};
|
`${label}-${maxWidth}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}-${config.joinWith}`
|
||||||
|
);
|
||||||
|
|
||||||
const breakString = (word, maxWidth, hyphenCharacter = '-', config) => {
|
const breakString = memoize(
|
||||||
if (!breakString.cache) {
|
(word, maxWidth, hyphenCharacter = '-', config) => {
|
||||||
breakString.cache = {};
|
config = Object.assign(
|
||||||
}
|
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 0 },
|
||||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
config
|
||||||
const cacheKey = `${word}-${maxWidth}-${hyphenCharacter}-${JSON.stringify(config)}`;
|
);
|
||||||
if (breakString.cache[cacheKey]) {
|
|
||||||
return breakString.cache[cacheKey];
|
|
||||||
}
|
|
||||||
const characters = word.split('');
|
const characters = word.split('');
|
||||||
const lines = [];
|
const lines = [];
|
||||||
let currentLine = '';
|
let currentLine = '';
|
||||||
@@ -569,10 +571,11 @@ const breakString = (word, maxWidth, hyphenCharacter = '-', config) => {
|
|||||||
currentLine = nextLine;
|
currentLine = nextLine;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const result = { hyphenatedStrings: lines, remainingWord: currentLine };
|
return { hyphenatedStrings: lines, remainingWord: currentLine };
|
||||||
breakString.cache[cacheKey] = result;
|
},
|
||||||
return result;
|
(word, maxWidth, hyphenCharacter = '-', config) =>
|
||||||
};
|
`${word}-${maxWidth}-${hyphenCharacter}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This calculates the text's height, taking into account the wrap breaks and
|
* This calculates the text's height, taking into account the wrap breaks and
|
||||||
@@ -583,10 +586,13 @@ const breakString = (word, maxWidth, hyphenCharacter = '-', config) => {
|
|||||||
*
|
*
|
||||||
* @return - The height for the given text
|
* @return - The height for the given text
|
||||||
* @param text the text to measure
|
* @param text the text to measure
|
||||||
* @param config - the config for fontSize, fontFamily, fontWeight, and margin all impacting the resulting size
|
* @param config - the config for fontSize, fontFamily, and fontWeight all impacting the resulting size
|
||||||
*/
|
*/
|
||||||
export const calculateTextHeight = function(text, config) {
|
export const calculateTextHeight = function(text, config) {
|
||||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
config = Object.assign(
|
||||||
|
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 15 },
|
||||||
|
config
|
||||||
|
);
|
||||||
return calculateTextDimensions(text, config).height;
|
return calculateTextDimensions(text, config).height;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -595,7 +601,7 @@ export const calculateTextHeight = function(text, config) {
|
|||||||
*
|
*
|
||||||
* @return - The width for the given text
|
* @return - The width for the given text
|
||||||
* @param text - The text to calculate the width of
|
* @param text - The text to calculate the width of
|
||||||
* @param config - the config for fontSize, fontFamily, fontWeight, and margin all impacting the resulting size
|
* @param config - the config for fontSize, fontFamily, and fontWeight all impacting the resulting size
|
||||||
*/
|
*/
|
||||||
export const calculateTextWidth = function(text, config) {
|
export const calculateTextWidth = function(text, config) {
|
||||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
||||||
@@ -609,25 +615,19 @@ export const calculateTextWidth = function(text, config) {
|
|||||||
* @param text - The text to calculate the width of
|
* @param text - The text to calculate the width of
|
||||||
* @param config - the config for fontSize, fontFamily, fontWeight, and margin all impacting the resulting size
|
* @param config - the config for fontSize, fontFamily, fontWeight, and margin all impacting the resulting size
|
||||||
*/
|
*/
|
||||||
export const calculateTextDimensions = function(text, config) {
|
export const calculateTextDimensions = memoize(
|
||||||
if (!calculateTextDimensions.cache) {
|
function(text, config) {
|
||||||
calculateTextDimensions.cache = {};
|
|
||||||
}
|
|
||||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
||||||
const { fontSize, fontFamily, fontWeight } = config;
|
const { fontSize, fontFamily, fontWeight } = config;
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return { width: 0, height: 0 };
|
return { width: 0, height: 0 };
|
||||||
}
|
}
|
||||||
const cacheKey = `${text}-${JSON.stringify(config)}`;
|
|
||||||
if (calculateTextDimensions.cache[cacheKey]) {
|
|
||||||
return calculateTextDimensions.cache[cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't really know if the user supplied font family will render on the user agent;
|
// We can't really know if the user supplied font family will render on the user agent;
|
||||||
// thus, we'll take the max width between the user supplied font family, and a default
|
// thus, we'll take the max width between the user supplied font family, and a default
|
||||||
// of sans-serif.
|
// of sans-serif.
|
||||||
const fontFamilies = ['sans-serif', fontFamily];
|
const fontFamilies = ['sans-serif', fontFamily];
|
||||||
const lines = common.splitBreaks(text);
|
const lines = text.split(common.lineBreakRegex);
|
||||||
let dims = [];
|
let dims = [];
|
||||||
|
|
||||||
const body = select('body');
|
const body = select('body');
|
||||||
@@ -670,10 +670,10 @@ export const calculateTextDimensions = function(text, config) {
|
|||||||
dims[0].lineHeight > dims[1].lineHeight)
|
dims[0].lineHeight > dims[1].lineHeight)
|
||||||
? 0
|
? 0
|
||||||
: 1;
|
: 1;
|
||||||
const result = dims[index];
|
return dims[index];
|
||||||
calculateTextDimensions.cache[cacheKey] = result;
|
},
|
||||||
return result;
|
(text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
|
||||||
};
|
);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
assignWithDepth,
|
assignWithDepth,
|
||||||
@@ -692,5 +692,6 @@ export default {
|
|||||||
getStylesFromArray,
|
getStylesFromArray,
|
||||||
generateId,
|
generateId,
|
||||||
random,
|
random,
|
||||||
|
memoize,
|
||||||
runFunc
|
runFunc
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,6 +69,25 @@ describe('when assignWithDepth: should merge objects within objects', function()
|
|||||||
expect(result).toEqual({ foo: "foo", bar: { foo: 'foo', bar: { foo: { message: 'this', willbe: 'present' } } }, foobar: "foobar", boofar: 1 });
|
expect(result).toEqual({ foo: "foo", bar: { foo: 'foo', bar: { foo: { message: 'this', willbe: 'present' } } }, foobar: "foobar", boofar: 1 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('when memoizing', function() {
|
||||||
|
it('should return the same value', function() {
|
||||||
|
const fib = utils.memoize(function(n, canary) {
|
||||||
|
canary.flag = true;
|
||||||
|
if (n < 2){
|
||||||
|
return 1;
|
||||||
|
}else{
|
||||||
|
//We'll console.log a loader every time we have to recurse
|
||||||
|
return fib(n-2, canary) + fib(n-1, canary);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let canary = {flag: false};
|
||||||
|
fib(10, canary);
|
||||||
|
expect(canary.flag).toBe(true);
|
||||||
|
canary = {flag: false};
|
||||||
|
fib(10, canary);
|
||||||
|
expect(canary.flag).toBe(false);
|
||||||
|
});
|
||||||
|
})
|
||||||
describe('when detecting chart type ', function() {
|
describe('when detecting chart type ', function() {
|
||||||
it('should handle a graph definition', function() {
|
it('should handle a graph definition', function() {
|
||||||
const str = 'graph TB\nbfs1:queue';
|
const str = 'graph TB\nbfs1:queue';
|
||||||
|
|||||||
Reference in New Issue
Block a user