zotero-better-notes/addon/chrome/content/bubbleMap.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>