341 lines
12 KiB
HTML
341 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<body>
|
|
<script src="chrome://__addonRef__/content/lib/js/go.js"></script>
|
|
<style>
|
|
html,
|
|
body,
|
|
div {
|
|
height: 100%;
|
|
width: 100%;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
#canvas {
|
|
position: absolute;
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
</style>
|
|
<div id="allSampleContent" class="p-4 w-full">
|
|
<script id="code">
|
|
// This variation on ForceDirectedLayout does not move any selected Nodes
|
|
// but does move all other nodes (vertexes).
|
|
class ContinuousForceDirectedLayout extends go.ForceDirectedLayout {
|
|
isFixed(v) {
|
|
return v.node.isSelected;
|
|
}
|
|
|
|
// optimization: reuse the ForceDirectedNetwork rather than re-create it each time
|
|
doLayout(coll) {
|
|
if (!this._isObserving) {
|
|
this._isObserving = true;
|
|
// cacheing the network means we need to recreate it if nodes or links have been added or removed or relinked,
|
|
// so we need to track structural model changes to discard the saved network.
|
|
this.diagram.addModelChangedListener((e) => {
|
|
// modelChanges include a few cases that we don't actually care about, such as
|
|
// "nodeCategory" or "linkToPortId", but we'll go ahead and recreate the network anyway.
|
|
// Also clear the network when replacing the model.
|
|
if (
|
|
e.modelChange !== "" ||
|
|
(e.change === go.ChangedEvent.Transaction &&
|
|
e.propertyName === "StartingFirstTransaction")
|
|
) {
|
|
this.network = null;
|
|
}
|
|
});
|
|
}
|
|
var net = this.network;
|
|
if (net === null) {
|
|
// the first time, just create the network as normal
|
|
this.network = net = this.makeNetwork(coll);
|
|
} else {
|
|
// but on reuse we need to update the LayoutVertex.bounds for selected nodes
|
|
this.diagram.nodes.each((n) => {
|
|
var v = net.findVertex(n);
|
|
if (v !== null) v.bounds = n.actualBounds;
|
|
});
|
|
}
|
|
// now perform the normal layout
|
|
super.doLayout(coll);
|
|
// doLayout normally discards the LayoutNetwork by setting Layout.network to null;
|
|
// here we remember it for next time
|
|
this.network = net;
|
|
}
|
|
}
|
|
// end ContinuousForceDirectedLayout
|
|
var Diagram;
|
|
|
|
function init() {
|
|
window.addEventListener("message", handler, false);
|
|
|
|
// Since 2.2 you can also author concise templates with method chaining instead of GraphObject.make
|
|
// For details, see https://gojs.net/latest/intro/buildingObjects.html
|
|
const $ = go.GraphObject.make; // for conciseness in defining templates
|
|
|
|
Diagram = $(
|
|
go.Diagram,
|
|
"DiagramDiv", // must name or refer to the DIV HTML element
|
|
{
|
|
initialAutoScale: go.Diagram.UniformToFill, // an initial automatic zoom-to-fit
|
|
contentAlignment: go.Spot.Center, // align document to the center of the viewport
|
|
layout: $(
|
|
ContinuousForceDirectedLayout, // automatically spread nodes apart while dragging
|
|
{ defaultSpringLength: 15, defaultElectricalCharge: 30 }
|
|
),
|
|
// do an extra layout at the end of a move
|
|
SelectionMoved: (e) => e.diagram.layout.invalidateLayout(),
|
|
}
|
|
);
|
|
|
|
// dragging a node invalidates the Diagram.layout, causing a layout during the drag
|
|
Diagram.toolManager.draggingTool.doMouseMove = function () {
|
|
go.DraggingTool.prototype.doMouseMove.call(this);
|
|
if (this.isActive) {
|
|
this.diagram.layout.invalidateLayout();
|
|
}
|
|
};
|
|
|
|
// define each Node's appearance
|
|
Diagram.nodeTemplate = $(
|
|
go.Node,
|
|
"Vertical", // the whole node panel
|
|
// define the node's outer shape, which will surround the TextBlock
|
|
$(
|
|
go.Panel,
|
|
"Spot",
|
|
$(
|
|
go.Shape,
|
|
"Circle",
|
|
{
|
|
name: "SHAPE",
|
|
fill: "lightgray", // default value, but also data-bound
|
|
strokeWidth: 0,
|
|
desiredSize: new go.Size(10, 10),
|
|
},
|
|
new go.Binding("fill", "isSelected", (s, obj) =>
|
|
s ? "#f2ac46" : "lightgray"
|
|
).ofObject()
|
|
)
|
|
),
|
|
$(
|
|
go.TextBlock,
|
|
{
|
|
textAlign: "center",
|
|
maxSize: new go.Size(100, NaN),
|
|
editable: true,
|
|
textEdited: editNode,
|
|
},
|
|
new go.Binding("text", "text").makeTwoWay(),
|
|
new go.Binding("stroke", "stroke")
|
|
)
|
|
);
|
|
// the rest of this app is the same as samples/conceptMap.html
|
|
|
|
// selected nodes show a button for adding children
|
|
Diagram.nodeTemplate.selectionAdornmentTemplate = $(
|
|
go.Adornment,
|
|
"Spot",
|
|
$(
|
|
go.Panel,
|
|
"Auto",
|
|
// this Adornment has a rectangular blue Shape around the selected node
|
|
$(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 3 }),
|
|
$(go.Placeholder, { margin: new go.Margin(4, 4, 0, 4) })
|
|
),
|
|
// and this Adornment has a Button to the right of the selected node
|
|
$(
|
|
"Button",
|
|
{
|
|
alignment: go.Spot.Right,
|
|
alignmentFocus: go.Spot.Left,
|
|
click: jumpNode, // define click behavior for this Button in the Adornment
|
|
},
|
|
$(
|
|
go.TextBlock,
|
|
"↗️", // the Button content
|
|
{ font: "bold 8pt sans-serif" }
|
|
)
|
|
)
|
|
);
|
|
|
|
// replace the default Link template in the linkTemplateMap
|
|
Diagram.linkTemplate = $(
|
|
go.Link, // the whole link panel
|
|
$(
|
|
go.Shape, // the link shape
|
|
{ stroke: "grey" }
|
|
),
|
|
$(
|
|
go.Shape, // the arrowhead
|
|
{ toArrow: "standard", stroke: null }
|
|
),
|
|
$(go.Panel, "Auto")
|
|
);
|
|
window.parent.postMessage({ type: "ready" }, "*");
|
|
getData();
|
|
}
|
|
|
|
function getData() {
|
|
window.parent.postMessage({ type: "getMindMapData" }, "*");
|
|
}
|
|
|
|
function setData(nodes) {
|
|
const nodeDataArray = [{ key: 999, text: "📄", parent: undefined }];
|
|
const linkDataArray = [];
|
|
const wrapText = (text) => {
|
|
let wrappedText = "";
|
|
for (let i = 0; i < text.length / 50; i++) {
|
|
wrappedText += text.slice(50 * i, 50 * i + 50);
|
|
wrappedText += "\n";
|
|
}
|
|
return wrappedText;
|
|
};
|
|
for (const node of nodes) {
|
|
const parent =
|
|
node.parent.model.id === -1 ? 999 : node.parent.model.id;
|
|
nodeDataArray.push({
|
|
key: node.model.id,
|
|
text: `${node.model.level === 7 ? "🔗" : ""}${wrapText(
|
|
node.model.name
|
|
)}`,
|
|
stroke: "-moz-DialogText",
|
|
lineIndex: node.model.lineIndex,
|
|
level: node.model.level,
|
|
noteLink: node.model.level === 7 ? node.model.link : "",
|
|
});
|
|
linkDataArray.push({
|
|
from: parent,
|
|
to: node.model.id,
|
|
text: "",
|
|
});
|
|
}
|
|
console.log(nodeDataArray, linkDataArray);
|
|
Diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
|
|
}
|
|
|
|
function jumpNode(e, obj) {
|
|
var adorn = obj.part;
|
|
var oldnode = adorn.adornedPart;
|
|
var olddata = oldnode.data;
|
|
if (olddata.noteLink) {
|
|
window.parent.postMessage(
|
|
{ type: "openNote", link: olddata.noteLink, id: olddata.key },
|
|
"*"
|
|
);
|
|
} else {
|
|
window.parent.postMessage(
|
|
{
|
|
type: "jumpNode",
|
|
lineIndex: olddata.lineIndex,
|
|
id: olddata.key,
|
|
workspaceType: window.workspaceType || "tab",
|
|
},
|
|
"*"
|
|
);
|
|
}
|
|
}
|
|
|
|
function editNode(textBlock, previousText, currentText) {
|
|
const adorn = textBlock.part;
|
|
const data = adorn.data;
|
|
if (data.level === 7) {
|
|
alert("Link cannot be edited in mind map");
|
|
return false;
|
|
}
|
|
window.parent.postMessage(
|
|
{
|
|
type: "editNode",
|
|
lineIndex: data.lineIndex,
|
|
text: data.text,
|
|
workspaceType: window.workspaceType || "tab",
|
|
},
|
|
"*"
|
|
);
|
|
}
|
|
|
|
function handler(e) {
|
|
console.log(e);
|
|
switch (e.data.type) {
|
|
case "setMindMapData":
|
|
setData(e.data.nodes);
|
|
window.workspaceType = e.data.workspaceType;
|
|
break;
|
|
case "saveImage":
|
|
const imgString = Diagram.makeImageData({
|
|
scale: 1,
|
|
});
|
|
window.parent.postMessage(
|
|
{
|
|
type: "saveImageReturn",
|
|
image: imgString,
|
|
},
|
|
"*"
|
|
);
|
|
case "saveSVG":
|
|
const svgElement = Diagram.makeSvg({
|
|
scale: 1,
|
|
});
|
|
window.parent.postMessage(
|
|
{
|
|
type: "saveSVGReturn",
|
|
image: svgElement.outerHTML,
|
|
},
|
|
"*"
|
|
);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
window.addEventListener("DOMContentLoaded", init);
|
|
|
|
var resizeTime = new Date().getTime();
|
|
window.addEventListener("resize", (e) => {
|
|
const canvas = document.getElementsByTagName("canvas")[0];
|
|
const div = document.getElementById("DiagramDiv");
|
|
canvas.setAttribute("height", div.getAttribute("height"));
|
|
canvas.setAttribute("width", div.getAttribute("width"));
|
|
const currentResize = new Date().getTime();
|
|
resizeTime = currentResize;
|
|
// Delay update
|
|
setTimeout(() => {
|
|
if (resizeTime === currentResize) {
|
|
getData();
|
|
}
|
|
}, 500);
|
|
});
|
|
</script>
|
|
|
|
<div id="sample">
|
|
<div
|
|
id="DiagramDiv"
|
|
style="
|
|
width: 100%;
|
|
position: relative;
|
|
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
|
|
cursor: auto;
|
|
font: 13px sans-serif;
|
|
overflow: hidden;
|
|
"
|
|
>
|
|
<canvas
|
|
tabindex="0"
|
|
style="
|
|
position: absolute;
|
|
top: 0px;
|
|
left: 0px;
|
|
z-index: 2;
|
|
user-select: none;
|
|
touch-action: none;
|
|
cursor: auto;
|
|
"
|
|
>This text is displayed if your browser does not support the Canvas
|
|
HTML element.</canvas
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|