zotero-better-notes/addon/chrome/content/mindMap.html

409 lines
14 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">
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;
Diagram = $(go.Diagram, "DiagramDiv", {
"commandHandler.copiesTree": true,
"commandHandler.copiesParentKey": true,
"commandHandler.deletesTree": true,
"draggingTool.dragsTree": true,
"undoManager.isEnabled": true,
initialAutoScale: go.Diagram.UniformToFill,
layout: $(go.TreeLayout, {
comparer: go.LayoutVertex.smartComparer,
}),
});
// a node consists of some text with a line shape underneath
Diagram.nodeTemplate = $(
go.Node,
"Vertical",
{ selectionObjectName: "TEXT" },
$(
go.TextBlock,
{
name: "TEXT",
minSize: new go.Size(30, 15),
editable: true,
textEdited: editNode,
},
// remember not only the text string but the scale and the font in the node data
new go.Binding("text", "text").makeTwoWay(),
new go.Binding("scale", "scale").makeTwoWay(),
new go.Binding("font", "font").makeTwoWay(),
new go.Binding("stroke", "stroke").makeTwoWay()
),
$(
go.Shape,
"LineH",
{
stretch: go.GraphObject.Horizontal,
strokeWidth: 3,
height: 3,
// this line shape is the port -- what links connect with
portId: "",
fromSpot: go.Spot.LeftRightSides,
toSpot: go.Spot.LeftRightSides,
},
new go.Binding("stroke", "brush"),
// make sure links come in from the proper direction and go out appropriately
new go.Binding("fromSpot", "dir", (d) => spotConverter(d, true)),
new go.Binding("toSpot", "dir", (d) => spotConverter(d, false))
),
// remember the locations of each node in the node data
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(
go.Point.stringify
),
// make sure text "grows" in the desired direction
new go.Binding("locationSpot", "dir", (d) =>
spotConverter(d, false)
)
);
// 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" }
)
)
);
// a link is just a Bezier-curved line of the same color as the node to which it is connected
Diagram.linkTemplate = $(
go.Link,
{
curve: go.Link.Bezier,
fromShortLength: -2,
toShortLength: -2,
selectable: false,
},
$(
go.Shape,
{ strokeWidth: 3 },
new go.Binding("stroke", "toNode", (n) => {
if (n.data.brush) return n.data.brush;
return "black";
}).ofObject()
)
);
Diagram.addDiagramListener("SelectionMoved", (e) => {
var rootX = Diagram.findNodeForKey(0).location.x;
Diagram.selection.each((node) => {
if (node.data.parent !== 0) return; // Only consider nodes connected to the root
var nodeX = node.location.x;
if (rootX < nodeX && node.data.dir !== "right") {
updateNodeDirection(node, "right");
} else if (rootX > nodeX && node.data.dir !== "left") {
updateNodeDirection(node, "left");
}
layoutTree(node);
});
});
// read in the predefined graph using the JSON format data held in the "mySavedModel" textarea
window.parent.postMessage({ type: "ready" }, "*");
getData();
}
function spotConverter(dir, from) {
if (dir === "left") {
return from ? go.Spot.Left : go.Spot.Right;
} else {
return from ? go.Spot.Right : go.Spot.Left;
}
}
function changeTextSize(obj, factor) {
var adorn = obj.part;
adorn.diagram.startTransaction("Change Text Size");
var node = adorn.adornedPart;
var tb = node.findObject("TEXT");
tb.scale *= factor;
adorn.diagram.commitTransaction("Change Text Size");
}
function toggleTextWeight(obj) {
var adorn = obj.part;
adorn.diagram.startTransaction("Change Text Weight");
var node = adorn.adornedPart;
var tb = node.findObject("TEXT");
// assume "bold" is at the start of the font specifier
var idx = tb.font.indexOf("bold");
if (idx < 0) {
tb.font = "bold " + tb.font;
} else {
tb.font = tb.font.slice(idx + 5);
}
adorn.diagram.commitTransaction("Change Text Weight");
}
function updateNodeDirection(node, dir) {
Diagram.model.setDataProperty(node.data, "dir", dir);
// recursively update the direction of the child nodes
var chl = node.findTreeChildrenNodes(); // gives us an iterator of the child nodes related to this particular node
while (chl.next()) {
updateNodeDirection(chl.value, dir);
}
}
function layoutTree(node) {
if (node.data.key === 0) {
// adding to the root?
layoutAll(); // lay out everything
} else {
// otherwise lay out only the subtree starting at this parent node
var parts = node.findTreeParts();
layoutAngle(parts, node.data.dir === "left" ? 180 : 0);
}
}
function layoutAngle(parts, angle) {
var layout = go.GraphObject.make(go.TreeLayout, {
angle: angle,
arrangement: go.TreeLayout.ArrangementFixedRoots,
nodeSpacing: 5,
layerSpacing: 20,
setsPortSpot: false, // don't set port spots since we're managing them with our spotConverter function
setsChildPortSpot: false,
});
layout.doLayout(parts);
}
function layoutAll() {
var root = Diagram.findNodeForKey(0);
if (root === null) return;
Diagram.startTransaction("Layout");
// split the nodes and links into two collections
var rightward = new go.Set(/*go.Part*/);
var leftward = new go.Set(/*go.Part*/);
root.findLinksConnected().each((link) => {
var child = link.toNode;
if (child.data.dir === "left") {
leftward.add(root); // the root node is in both collections
leftward.add(link);
leftward.addAll(child.findTreeParts());
} else {
rightward.add(root); // the root node is in both collections
rightward.add(link);
rightward.addAll(child.findTreeParts());
}
});
// do one layout and then the other without moving the shared root node
layoutAngle(rightward, 0);
layoutAngle(leftward, 180);
Diagram.commitTransaction("Layout");
}
function getData() {
window.parent.postMessage({ type: "getMindMapData" }, "*");
}
function setData(nodes) {
const data = {
class: "go.TreeModel",
nodeDataArray: [{ key: 999, text: "📄", parent: undefined }],
};
const colors = [];
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;
data.nodeDataArray.push({
key: node.model.id,
text: `${node.model.level === 7 ? "🔗" : ""}${wrapText(
node.model.name
)}`,
stroke: "-moz-DialogText",
parent: parent,
lineIndex: node.model.lineIndex,
level: node.model.level,
noteLink: node.model.level === 7 ? node.model.link : "",
brush: go.Brush.randomColor(),
});
}
Diagram.model = go.Model.fromJson(data);
}
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", () => {
try {
init();
} catch (e) {
window.parent.postMessage({ type: "error", event: e }, "*");
}
});
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>