mindoc/static/editor.md/lib/mindmap/view.js

1193 lines
33 KiB
Go
Raw Normal View History

/*! 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));