mindoc/static/editor.md/lib/mindmap/view.js
zhangsheng 1290dea30d feature-mindmap: editor.md add mindmap
主要在markdown编辑器中增加思维导图渲染方式,引入markmap开源组件
2021-09-24 14:37:52 +00:00

1193 lines
33 KiB
Go
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*! markmap-lib v0.7.8 | MIT License */
(function (exports, d3) {
'use strict';
function count(node) {
var sum = 0,
children = node.children,
i = children && children.length;
if (!i) sum = 1;
else while (--i >= 0) sum += children[i].value;
node.value = sum;
}
function node_count() {
return this.eachAfter(count);
}
function node_each(callback) {
var node = this, current, next = [node], children, i, n;
do {
current = next.reverse(), next = [];
while (node = current.pop()) {
callback(node), children = node.children;
if (children) for (i = 0, n = children.length; i < n; ++i) {
next.push(children[i]);
}
}
} while (next.length);
return this;
}
function node_eachBefore(callback) {
var node = this, nodes = [node], children, i;
while (node = nodes.pop()) {
callback(node), children = node.children;
if (children) for (i = children.length - 1; i >= 0; --i) {
nodes.push(children[i]);
}
}
return this;
}
function node_eachAfter(callback) {
var node = this, nodes = [node], next = [], children, i, n;
while (node = nodes.pop()) {
next.push(node), children = node.children;
if (children) for (i = 0, n = children.length; i < n; ++i) {
nodes.push(children[i]);
}
}
while (node = next.pop()) {
callback(node);
}
return this;
}
function node_sum(value) {
return this.eachAfter(function(node) {
var sum = +value(node.data) || 0,
children = node.children,
i = children && children.length;
while (--i >= 0) sum += children[i].value;
node.value = sum;
});
}
function node_sort(compare) {
return this.eachBefore(function(node) {
if (node.children) {
node.children.sort(compare);
}
});
}
function node_path(end) {
var start = this,
ancestor = leastCommonAncestor(start, end),
nodes = [start];
while (start !== ancestor) {
start = start.parent;
nodes.push(start);
}
var k = nodes.length;
while (end !== ancestor) {
nodes.splice(k, 0, end);
end = end.parent;
}
return nodes;
}
function leastCommonAncestor(a, b) {
if (a === b) return a;
var aNodes = a.ancestors(),
bNodes = b.ancestors(),
c = null;
a = aNodes.pop();
b = bNodes.pop();
while (a === b) {
c = a;
a = aNodes.pop();
b = bNodes.pop();
}
return c;
}
function node_ancestors() {
var node = this, nodes = [node];
while (node = node.parent) {
nodes.push(node);
}
return nodes;
}
function node_descendants() {
var nodes = [];
this.each(function(node) {
nodes.push(node);
});
return nodes;
}
function node_leaves() {
var leaves = [];
this.eachBefore(function(node) {
if (!node.children) {
leaves.push(node);
}
});
return leaves;
}
function node_links() {
var root = this, links = [];
root.each(function(node) {
if (node !== root) { // Dont include the roots parent, if any.
links.push({source: node.parent, target: node});
}
});
return links;
}
function hierarchy(data, children) {
var root = new Node(data),
valued = +data.value && (root.value = data.value),
node,
nodes = [root],
child,
childs,
i,
n;
if (children == null) children = defaultChildren;
while (node = nodes.pop()) {
if (valued) node.value = +node.data.value;
if ((childs = children(node.data)) && (n = childs.length)) {
node.children = new Array(n);
for (i = n - 1; i >= 0; --i) {
nodes.push(child = node.children[i] = new Node(childs[i]));
child.parent = node;
child.depth = node.depth + 1;
}
}
}
return root.eachBefore(computeHeight);
}
function node_copy() {
return hierarchy(this).eachBefore(copyData);
}
function defaultChildren(d) {
return d.children;
}
function copyData(node) {
node.data = node.data.data;
}
function computeHeight(node) {
var height = 0;
do node.height = height;
while ((node = node.parent) && (node.height < ++height));
}
function Node(data) {
this.data = data;
this.depth =
this.height = 0;
this.parent = null;
}
Node.prototype = hierarchy.prototype = {
constructor: Node,
count: node_count,
each: node_each,
eachAfter: node_eachAfter,
eachBefore: node_eachBefore,
sum: node_sum,
sort: node_sort,
path: node_path,
ancestors: node_ancestors,
descendants: node_descendants,
leaves: node_leaves,
links: node_links,
copy: node_copy
};
var version = "2.1.1";
const defaults = Object.freeze({
children: data => data.children,
nodeSize: node => node.data.size,
spacing: 0,
});
// Create a layout function with customizable options. Per D3-style, the
// options can be set at any time using setter methods. The layout function
// will compute the tree node positions based on the options in effect at the
// time it is called.
function flextree(options) {
const opts = Object.assign({}, defaults, options);
function accessor(name) {
const opt = opts[name];
return typeof opt === 'function' ? opt : () => opt;
}
function layout(tree) {
const wtree = wrap(getWrapper(), tree, node=>node.children);
wtree.update();
return wtree.data;
}
function getFlexNode() {
const nodeSize = accessor('nodeSize');
const spacing = accessor('spacing');
return class FlexNode extends hierarchy.prototype.constructor {
constructor(data) {
super(data);
}
copy() {
const c = wrap(this.constructor, this, node=>node.children);
c.each(node => node.data = node.data.data);
return c;
}
get size() { return nodeSize(this); }
spacing(oNode) { return spacing(this, oNode); }
get nodes() { return this.descendants(); }
get xSize() { return this.size[0]; }
get ySize() { return this.size[1]; }
get top() { return this.y; }
get bottom() { return this.y + this.ySize; }
get left() { return this.x - this.xSize / 2; }
get right() { return this.x + this.xSize / 2; }
get root() {
const ancs = this.ancestors();
return ancs[ancs.length - 1];
}
get numChildren() {
return this.hasChildren ? this.children.length : 0;
}
get hasChildren() { return !this.noChildren; }
get noChildren() { return this.children === null; }
get firstChild() {
return this.hasChildren ? this.children[0] : null;
}
get lastChild() {
return this.hasChildren ? this.children[this.numChildren - 1] : null;
}
get extents() {
return (this.children || []).reduce(
(acc, kid) => FlexNode.maxExtents(acc, kid.extents),
this.nodeExtents);
}
get nodeExtents() {
return {
top: this.top,
bottom: this.bottom,
left: this.left,
right: this.right,
};
}
static maxExtents(e0, e1) {
return {
top: Math.min(e0.top, e1.top),
bottom: Math.max(e0.bottom, e1.bottom),
left: Math.min(e0.left, e1.left),
right: Math.max(e0.right, e1.right),
};
}
};
}
function getWrapper() {
const FlexNode = getFlexNode();
const nodeSize = accessor('nodeSize');
const spacing = accessor('spacing');
return class extends FlexNode {
constructor(data) {
super(data);
Object.assign(this, {
x: 0, y: 0,
relX: 0, prelim: 0, shift: 0, change: 0,
lExt: this, lExtRelX: 0, lThr: null,
rExt: this, rExtRelX: 0, rThr: null,
});
}
get size() { return nodeSize(this.data); }
spacing(oNode) { return spacing(this.data, oNode.data); }
get x() { return this.data.x; }
set x(v) { this.data.x = v; }
get y() { return this.data.y; }
set y(v) { this.data.y = v; }
update() {
layoutChildren(this);
resolveX(this);
return this;
}
};
}
function wrap(FlexClass, treeData, children) {
const _wrap = (data, parent) => {
const node = new FlexClass(data);
Object.assign(node, {
parent,
depth: parent === null ? 0 : parent.depth + 1,
height: 0,
length: 1,
});
const kidsData = children(data) || [];
node.children = kidsData.length === 0 ? null
: kidsData.map(kd => _wrap(kd, node));
if (node.children) {
Object.assign(node, node.children.reduce(
(hl, kid) => ({
height: Math.max(hl.height, kid.height + 1),
length: hl.length + kid.length,
}), node
));
}
return node;
};
return _wrap(treeData, null);
}
Object.assign(layout, {
nodeSize(arg) {
return arguments.length ? (opts.nodeSize = arg, layout) : opts.nodeSize;
},
spacing(arg) {
return arguments.length ? (opts.spacing = arg, layout) : opts.spacing;
},
children(arg) {
return arguments.length ? (opts.children = arg, layout) : opts.children;
},
hierarchy(treeData, children) {
const kids = typeof children === 'undefined' ? opts.children : children;
return wrap(getFlexNode(), treeData, kids);
},
dump(tree) {
const nodeSize = accessor('nodeSize');
const _dump = i0 => node => {
const i1 = i0 + ' ';
const i2 = i0 + ' ';
const {x, y} = node;
const size = nodeSize(node);
const kids = (node.children || []);
const kdumps = (kids.length === 0) ? ' ' :
`,${i1}children: [${i2}${kids.map(_dump(i2)).join(i2)}${i1}],${i0}`;
return `{ size: [${size.join(', ')}],${i1}x: ${x}, y: ${y}${kdumps}},`;
};
return _dump('\n')(tree);
},
});
return layout;
}
flextree.version = version;
const layoutChildren = (w, y = 0) => {
w.y = y;
(w.children || []).reduce((acc, kid) => {
const [i, lastLows] = acc;
layoutChildren(kid, w.y + w.ySize);
// The lowest vertical coordinate while extreme nodes still point
// in current subtree.
const lowY = (i === 0 ? kid.lExt : kid.rExt).bottom;
if (i !== 0) separate(w, i, lastLows);
const lows = updateLows(lowY, i, lastLows);
return [i + 1, lows];
}, [0, null]);
shiftChange(w);
positionRoot(w);
return w;
};
// Resolves the relative coordinate properties - relX and prelim --
// to set the final, absolute x coordinate for each node. This also sets
// `prelim` to 0, so that `relX` for each node is its x-coordinate relative
// to its parent.
const resolveX = (w, prevSum, parentX) => {
// A call to resolveX without arguments is assumed to be for the root of
// the tree. This will set the root's x-coord to zero.
if (typeof prevSum === 'undefined') {
prevSum = -w.relX - w.prelim;
parentX = 0;
}
const sum = prevSum + w.relX;
w.relX = sum + w.prelim - parentX;
w.prelim = 0;
w.x = parentX + w.relX;
(w.children || []).forEach(k => resolveX(k, sum, w.x));
return w;
};
// Process shift and change for all children, to add intermediate spacing to
// each child's modifier.
const shiftChange = w => {
(w.children || []).reduce((acc, child) => {
const [lastShiftSum, lastChangeSum] = acc;
const shiftSum = lastShiftSum + child.shift;
const changeSum = lastChangeSum + shiftSum + child.change;
child.relX += changeSum;
return [shiftSum, changeSum];
}, [0, 0]);
};
// Separates the latest child from its previous sibling
/* eslint-disable complexity */
const separate = (w, i, lows) => {
const lSib = w.children[i - 1];
const curSubtree = w.children[i];
let rContour = lSib;
let rSumMods = lSib.relX;
let lContour = curSubtree;
let lSumMods = curSubtree.relX;
let isFirst = true;
while (rContour && lContour) {
if (rContour.bottom > lows.lowY) lows = lows.next;
// How far to the left of the right side of rContour is the left side
// of lContour? First compute the center-to-center distance, then add
// the "spacing"
const dist =
(rSumMods + rContour.prelim) - (lSumMods + lContour.prelim) +
rContour.xSize / 2 + lContour.xSize / 2 +
rContour.spacing(lContour);
if (dist > 0 || (dist < 0 && isFirst)) {
lSumMods += dist;
// Move subtree by changing relX.
moveSubtree(curSubtree, dist);
distributeExtra(w, i, lows.index, dist);
}
isFirst = false;
// Advance highest node(s) and sum(s) of modifiers
const rightBottom = rContour.bottom;
const leftBottom = lContour.bottom;
if (rightBottom <= leftBottom) {
rContour = nextRContour(rContour);
if (rContour) rSumMods += rContour.relX;
}
if (rightBottom >= leftBottom) {
lContour = nextLContour(lContour);
if (lContour) lSumMods += lContour.relX;
}
}
// Set threads and update extreme nodes. In the first case, the
// current subtree is taller than the left siblings.
if (!rContour && lContour) setLThr(w, i, lContour, lSumMods);
// In the next case, the left siblings are taller than the current subtree
else if (rContour && !lContour) setRThr(w, i, rContour, rSumMods);
};
/* eslint-enable complexity */
// Move subtree by changing relX.
const moveSubtree = (subtree, distance) => {
subtree.relX += distance;
subtree.lExtRelX += distance;
subtree.rExtRelX += distance;
};
const distributeExtra = (w, curSubtreeI, leftSibI, dist) => {
const curSubtree = w.children[curSubtreeI];
const n = curSubtreeI - leftSibI;
// Are there intermediate children?
if (n > 1) {
const delta = dist / n;
w.children[leftSibI + 1].shift += delta;
curSubtree.shift -= delta;
curSubtree.change -= dist - delta;
}
};
const nextLContour = w => {
return w.hasChildren ? w.firstChild : w.lThr;
};
const nextRContour = w => {
return w.hasChildren ? w.lastChild : w.rThr;
};
const setLThr = (w, i, lContour, lSumMods) => {
const firstChild = w.firstChild;
const lExt = firstChild.lExt;
const curSubtree = w.children[i];
lExt.lThr = lContour;
// Change relX so that the sum of modifier after following thread is correct.
const diff = lSumMods - lContour.relX - firstChild.lExtRelX;
lExt.relX += diff;
// Change preliminary x coordinate so that the node does not move.
lExt.prelim -= diff;
// Update extreme node and its sum of modifiers.
firstChild.lExt = curSubtree.lExt;
firstChild.lExtRelX = curSubtree.lExtRelX;
};
// Mirror image of setLThr.
const setRThr = (w, i, rContour, rSumMods) => {
const curSubtree = w.children[i];
const rExt = curSubtree.rExt;
const lSib = w.children[i - 1];
rExt.rThr = rContour;
const diff = rSumMods - rContour.relX - curSubtree.rExtRelX;
rExt.relX += diff;
rExt.prelim -= diff;
curSubtree.rExt = lSib.rExt;
curSubtree.rExtRelX = lSib.rExtRelX;
};
// Position root between children, taking into account their modifiers
const positionRoot = w => {
if (w.hasChildren) {
const k0 = w.firstChild;
const kf = w.lastChild;
const prelim = (k0.prelim + k0.relX - k0.xSize / 2 +
kf.relX + kf.prelim + kf.xSize / 2 ) / 2;
Object.assign(w, {
prelim,
lExt: k0.lExt, lExtRelX: k0.lExtRelX,
rExt: kf.rExt, rExtRelX: kf.rExtRelX,
});
}
};
// Make/maintain a linked list of the indexes of left siblings and their
// lowest vertical coordinate.
const updateLows = (lowY, index, lastLows) => {
// Remove siblings that are hidden by the new subtree.
while (lastLows !== null && lowY >= lastLows.lowY)
lastLows = lastLows.next;
// Prepend the new subtree.
return {
lowY,
index,
next: lastLows,
};
};
const uniqId = Math.random().toString(36).slice(2, 8);
let globalIndex = 0;
function getId() {
globalIndex += 1;
return `mm-${uniqId}-${globalIndex}`;
}
function walkTree(tree, callback, key = 'c') {
const walk = (item, parent) => callback(item, () => {
var _item$key;
(_item$key = item[key]) == null ? void 0 : _item$key.forEach(child => {
walk(child, item);
});
}, parent);
walk(tree);
}
function arrayFrom(arrayLike) {
if (Array.from) return Array.from(arrayLike);
const array = [];
for (let i = 0; i < arrayLike.length; i += 1) {
array.push(arrayLike[i]);
}
return array;
}
function flatMap(arrayLike, callback) {
if (arrayLike.flatMap) return arrayLike.flatMap(callback);
const array = [];
for (let i = 0; i < arrayLike.length; i += 1) {
const result = callback(arrayLike[i], i, arrayLike);
if (Array.isArray(result)) array.push(...result);else array.push(result);
}
return array;
}
function addClass(className, ...rest) {
const classList = (className || '').split(' ').filter(Boolean);
rest.forEach(item => {
if (item && classList.indexOf(item) < 0) classList.push(item);
});
return classList.join(' ');
}
function childSelector(filter) {
if (typeof filter === 'string') {
const tagName = filter;
filter = el => el.tagName === tagName;
}
const filterFn = filter;
return function selector() {
let nodes = arrayFrom(this.childNodes);
if (filterFn) nodes = nodes.filter(node => filterFn(node));
return nodes;
};
}
function memoize(fn) {
const cache = {};
return function memoized(...args) {
const key = `${args[0]}`;
let data = cache[key];
if (!data) {
data = {
value: fn(...args)
};
cache[key] = data;
}
return data.value;
};
}
function createElement(tagName, props, attrs) {
const el = document.createElement(tagName);
if (props) {
Object.entries(props).forEach(([key, value]) => {
el[key] = value;
});
}
if (attrs) {
Object.entries(attrs).forEach(([key, value]) => {
el.setAttribute(key, value);
});
}
return el;
}
const memoizedPreloadJS = memoize(url => {
document.head.append(createElement('link', {
rel: 'preload',
as: 'script',
href: url
}));
});
function loadJSItem(item, context) {
if (item.type === 'script') {
return new Promise((resolve, reject) => {
document.head.append(createElement('script', Object.assign(Object.assign({}, item.data), {}, {
onload: resolve,
onerror: reject
})));
});
} else if (item.type === 'iife') {
const {
fn,
getParams
} = item.data;
fn(...((getParams == null ? void 0 : getParams(context)) || []));
}
}
function loadCSSItem(item) {
if (item.type === 'style') {
document.head.append(createElement('style', {
textContent: item.data
}));
} else if (item.type === 'stylesheet') {
document.head.append(createElement('link', Object.assign({
rel: 'stylesheet'
}, item.data)));
}
}
async function loadJS(items, options) {
const needPreload = items.filter(item => item.type === 'script');
if (needPreload.length > 1) needPreload.forEach(item => memoizedPreloadJS(item.data.src));
for (const item of items) {
await loadJSItem(item, options);
}
}
function loadCSS(items) {
for (const item of items) {
loadCSSItem(item);
}
}
async function initializePlugins(Markmap, plugins, options) {
options = Object.assign({}, options);
await Promise.all(plugins.map(plugin => {
loadCSS(plugin.styles);
return loadJS(plugin.scripts, options);
}));
for (const {
initialize
} of plugins) {
if (initialize) initialize(Markmap, options);
}
}
const styles = [];
const scripts = [{
type: 'iife',
data: {
fn: mathJax => {
mathJax.options = Object.assign({
skipHtmlTags: {
'[-]': ['code', 'pre']
}
}, mathJax.options);
mathJax.startup = Object.assign({
typeset: false
}, mathJax.startup);
window.MathJax = mathJax;
},
getParams: context => [Object.assign({}, context.mathJax)]
}
}, {
type: 'script',
data: {
src: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js'
}
}];
function initialize(Markmap, options) {
Markmap.transformHtml.tap((mm, nodes) => {
var _MathJax$typeset, _MathJax;
(_MathJax$typeset = (_MathJax = window.MathJax).typeset) == null ? void 0 : _MathJax$typeset.call(_MathJax, nodes);
});
}
const plugin = {
styles,
scripts,
initialize
};
const styles$1 = [{
type: 'stylesheet',
data: {
href: 'https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.css'
}
}];
const scripts$1 = [{
type: 'iife',
data: {
fn: () => {
window.Prism = {
manual: true
};
}
}
}, {
type: 'script',
data: {
src: 'https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js'
}
}, // components will be added by paths relative to path of autoloader
{
type: 'script',
data: {
src: 'https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js'
}
}];
function initialize$1(Markmap, options) {
Markmap.transformHtml.tap((mm, nodes) => {
const {
Prism
} = window;
const langs = flatMap(nodes, node => arrayFrom(node.querySelectorAll('code[class*=language-]'))).map(code => {
const lang = code.className.match(/(?:^|\s)language-(\S+)|$/)[1];
if (Prism.languages[lang]) {
Prism.highlightElement(code);
} else {
return lang;
}
}).filter(Boolean);
if (langs.length) {
Prism.plugins.autoloader.loadLanguages(langs, () => {
mm.setData();
mm.fit();
});
}
});
}
const plugin$1 = {
styles: styles$1,
scripts: scripts$1,
initialize: initialize$1
};
var plugins = /*#__PURE__*/Object.freeze({
__proto__: null,
mathJax: plugin,
prism: plugin$1
});
class Hook {
constructor() {
this.listeners = [];
}
tap(fn) {
this.listeners.push(fn);
}
call(...args) {
for (const fn of this.listeners) {
fn(...args);
}
}
}
function linkWidth(nodeData) {
const data = nodeData.data;
return Math.max(6 - 2 * data.d, 1.5);
}
function adjustSpacing(tree, spacing) {
walkTree(tree, (d, next) => {
d.ySizeInner = d.ySize - spacing;
d.y += spacing;
next();
}, 'children');
}
class Markmap {
constructor(svg, opts) {
this.options = void 0;
this.state = void 0;
this.svg = void 0;
this.styleNode = void 0;
this.g = void 0;
this.zoom = void 0;
['handleZoom', 'handleClick'].forEach(key => {
this[key] = this[key].bind(this);
});
this.svg = svg.datum ? svg : d3.select(svg);
this.styleNode = this.svg.append('style');
this.zoom = d3.zoom().on('zoom', this.handleZoom);
this.options = Object.assign({
duration: 500,
nodeFont: '300 16px/20px sans-serif',
nodeMinHeight: 16,
spacingVertical: 5,
spacingHorizontal: 80,
autoFit: false,
fitRatio: 0.95,
color: (colorFn => node => colorFn(node.p.i))(d3.scaleOrdinal(d3.schemeCategory10)),
paddingX: 8
}, opts);
this.state = {
id: this.options.id || getId()
};
this.g = this.svg.append('g').attr('class', `${this.state.id}-g`);
this.updateStyle();
this.svg.call(this.zoom);
}
getStyleContent() {
const {
style,
nodeFont
} = this.options;
const {
id
} = this.state;
const extraStyle = typeof style === 'function' ? style(id) : '';
const styleText = `\
.${id} a { color: #0097e6; }
.${id} a:hover { color: #00a8ff; }
.${id}-g > path { fill: none; }
.${id}-fo > div { font: ${nodeFont}; white-space: nowrap; }
.${id}-fo code { padding: .2em .4em; font-size: calc(1em - 2px); color: #555; background-color: #f0f0f0; border-radius: 2px; }
.${id}-fo del { text-decoration: line-through; }
.${id}-fo em { font-style: italic; }
.${id}-fo strong { font-weight: 500; }
.${id}-fo pre { margin: 0; }
.${id}-fo pre[class*=language-] { padding: 0; }
.${id}-g > g { cursor: pointer; }
${extraStyle}
`;
return styleText;
}
updateStyle() {
this.svg.attr('class', addClass(this.svg.attr('class'), this.state.id));
this.styleNode.text(this.getStyleContent());
}
handleZoom() {
const {
transform
} = d3.event;
this.g.attr('transform', transform);
}
handleClick(d) {
var _data$p;
const {
data
} = d;
data.p = Object.assign(Object.assign({}, data.p), {}, {
f: !((_data$p = data.p) == null ? void 0 : _data$p.f)
});
this.renderData(d.data);
}
initializeData(node) {
let i = 0;
const {
nodeFont,
color,
nodeMinHeight
} = this.options;
const {
id
} = this.state;
const container = document.createElement('div');
const containerClass = `${id}-container`;
container.className = addClass(container.className, `${id}-fo`, containerClass);
const style = document.createElement('style');
style.textContent = `
${this.getStyleContent()}
.${containerClass} {
position: absolute;
width: 0;
height: 0;
top: -100px;
left: -100px;
overflow: hidden;
font: ${nodeFont};
}
.${containerClass} > div {
display: inline-block;
}
`;
document.body.append(style, container);
walkTree(node, (item, next) => {
var _item$c;
item.c = (_item$c = item.c) == null ? void 0 : _item$c.map(child => Object.assign({}, child));
i += 1;
const el = document.createElement('div');
el.innerHTML = item.v;
container.append(el);
item.p = Object.assign(Object.assign({}, item.p), {}, {
// unique ID
i,
el
});
color(item); // preload colors
next();
});
const nodes = arrayFrom(container.childNodes);
this.constructor.transformHtml.call(this, nodes);
walkTree(node, (item, next, parent) => {
var _parent$p;
const rect = item.p.el.getBoundingClientRect();
item.v = item.p.el.innerHTML;
item.p.s = [Math.ceil(rect.width), Math.max(Math.ceil(rect.height), nodeMinHeight)]; // TODO keep keys for unchanged objects
// unique key, should be based on content
item.p.k = `${(parent == null ? void 0 : (_parent$p = parent.p) == null ? void 0 : _parent$p.i) || ''}.${item.p.i}:${item.v}`;
next();
});
container.remove();
style.remove();
}
setOptions(opts) {
Object.assign(this.options, opts);
}
setData(data, opts) {
if (!data) data = Object.assign({}, this.state.data);
this.state.data = data;
this.initializeData(data);
if (opts) this.setOptions(opts);
this.renderData();
}
renderData(originData) {
var _origin$data$p$x, _origin$data$p$y;
if (!this.state.data) return;
const {
spacingHorizontal,
paddingX,
spacingVertical,
autoFit,
color
} = this.options;
const {
id
} = this.state;
const layout = flextree().children(d => {
var _d$p;
return !((_d$p = d.p) == null ? void 0 : _d$p.f) && d.c;
}).nodeSize(d => {
const [width, height] = d.data.p.s;
return [height, width + (width ? paddingX * 2 : 0) + spacingHorizontal];
}).spacing((a, b) => {
return a.parent === b.parent ? spacingVertical : spacingVertical * 2;
});
const tree = layout.hierarchy(this.state.data);
layout(tree);
adjustSpacing(tree, spacingHorizontal);
const descendants = tree.descendants().reverse();
const links = tree.links();
const linkShape = d3.linkHorizontal();
const minX = d3.min(descendants, d => d.x - d.xSize / 2);
const maxX = d3.max(descendants, d => d.x + d.xSize / 2);
const minY = d3.min(descendants, d => d.y);
const maxY = d3.max(descendants, d => d.y + d.ySizeInner);
Object.assign(this.state, {
minX,
maxX,
minY,
maxY
});
if (autoFit) this.fit();
const origin = originData && descendants.find(item => item.data === originData) || tree;
const x0 = (_origin$data$p$x = origin.data.p.x0) != null ? _origin$data$p$x : origin.x;
const y0 = (_origin$data$p$y = origin.data.p.y0) != null ? _origin$data$p$y : origin.y; // Update the nodes
const node = this.g.selectAll(childSelector('g')).data(descendants, d => d.data.p.k);
const nodeEnter = node.enter().append('g').attr('transform', d => `translate(${y0 + origin.ySizeInner - d.ySizeInner},${x0 + origin.xSize / 2 - d.xSize})`).on('click', this.handleClick);
const nodeExit = this.transition(node.exit());
nodeExit.select('rect').attr('width', 0).attr('x', d => d.ySizeInner);
nodeExit.select('foreignObject').style('opacity', 0);
nodeExit.attr('transform', d => `translate(${origin.y + origin.ySizeInner - d.ySizeInner},${origin.x + origin.xSize / 2 - d.xSize})`).remove();
const nodeMerge = node.merge(nodeEnter);
this.transition(nodeMerge).attr('transform', d => `translate(${d.y},${d.x - d.xSize / 2})`);
const rect = nodeMerge.selectAll(childSelector('rect')).data(d => [d], d => d.data.p.k).join(enter => {
return enter.append('rect').attr('x', d => d.ySizeInner).attr('y', d => d.xSize - linkWidth(d) / 2).attr('width', 0).attr('height', linkWidth);
}, update => update, exit => exit.remove());
this.transition(rect).attr('x', -1).attr('width', d => d.ySizeInner + 2).attr('fill', d => color(d.data));
const circle = nodeMerge.selectAll(childSelector('circle')).data(d => d.data.c ? [d] : [], d => d.data.p.k).join(enter => {
return enter.append('circle').attr('stroke-width', '1.5').attr('cx', d => d.ySizeInner).attr('cy', d => d.xSize).attr('r', 0);
}, update => update, exit => exit.remove());
this.transition(circle).attr('r', 6).attr('stroke', d => color(d.data)).attr('fill', d => {
var _d$data$p;
return ((_d$data$p = d.data.p) == null ? void 0 : _d$data$p.f) ? color(d.data) : '#fff';
});
const foreignObject = nodeMerge.selectAll(childSelector('foreignObject')).data(d => [d], d => d.data.p.k).join(enter => {
const fo = enter.append('foreignObject').attr('class', `${id}-fo`).attr('x', paddingX).attr('y', 0).style('opacity', 0).attr('height', d => d.xSize);
fo.append('xhtml:div').select(function (d) {
const node = d.data.p.el.cloneNode(true);
this.replaceWith(node);
return node;
}).attr('xmlns', 'http://www.w3.org/1999/xhtml');
return fo;
}, update => update, exit => exit.remove()).attr('width', d => Math.max(0, d.ySizeInner - paddingX * 2));
this.transition(foreignObject).style('opacity', 1); // Update the links
const path = this.g.selectAll(childSelector('path')).data(links, d => d.target.data.p.k).join(enter => {
const source = [y0 + origin.ySizeInner, x0 + origin.xSize / 2];
return enter.insert('path', 'g').attr('d', linkShape({
source,
target: source
}));
}, update => update, exit => {
const source = [origin.y + origin.ySizeInner, origin.x + origin.xSize / 2];
return this.transition(exit).attr('d', linkShape({
source,
target: source
})).remove();
});
this.transition(path).attr('stroke', d => color(d.target.data)).attr('stroke-width', d => linkWidth(d.target)).attr('d', d => {
const source = [d.source.y + d.source.ySizeInner, d.source.x + d.source.xSize / 2];
const target = [d.target.y, d.target.x + d.target.xSize / 2];
return linkShape({
source,
target
});
});
descendants.forEach(d => {
d.data.p.x0 = d.x;
d.data.p.y0 = d.y;
});
}
transition(sel) {
const {
duration
} = this.options;
return sel.transition().duration(duration);
}
fit() {
const svgNode = this.svg.node();
const {
width: offsetWidth,
height: offsetHeight
} = svgNode.getBoundingClientRect();
const {
fitRatio
} = this.options;
const {
minX,
maxX,
minY,
maxY
} = this.state;
const naturalWidth = maxY - minY;
const naturalHeight = maxX - minX;
const scale = Math.min(offsetWidth / naturalWidth * fitRatio, offsetHeight / naturalHeight * fitRatio, 2);
const initialZoom = d3.zoomIdentity.translate((offsetWidth - naturalWidth * scale) / 2 - minY * scale, (offsetHeight - naturalHeight * scale) / 2 - minX * scale).scale(scale);
return this.transition(this.svg).call(this.zoom.transform, initialZoom).end();
}
rescale(scale) {
const svgNode = this.svg.node();
const {
width: offsetWidth,
height: offsetHeight
} = svgNode.getBoundingClientRect();
const halfWidth = offsetWidth / 2;
const halfHeight = offsetHeight / 2;
const transform = d3.zoomTransform(svgNode);
const newTransform = transform.translate((halfWidth - transform.x) * (1 - scale) / transform.k, (halfHeight - transform.y) * (1 - scale) / transform.k).scale(scale);
return this.transition(this.svg).call(this.zoom.transform, newTransform).end();
}
static create(svg, opts, data) {
const mm = new Markmap(svg, opts);
if (data) {
mm.setData(data);
mm.fit(); // always fit for the first render
}
return mm;
}
}
Markmap.transformHtml = new Hook();
function markmap(svg, data, opts) {
return Markmap.create(svg, opts, data);
}
async function loadPlugins(items, options) {
items = items.map(item => {
if (typeof item === 'string') {
const name = item;
item = plugins[name];
if (!item) {
console.warn(`[markmap] Unknown plugin: ${name}`);
}
}
return item;
}).filter(Boolean);
return initializePlugins(Markmap, items, options);
}
exports.Markmap = Markmap;
exports.loadPlugins = loadPlugins;
exports.markmap = markmap;
exports.plugins = plugins;
}(this.markmap = this.markmap || {}, d3));