|
|
|
@ -1,5 +1,7 @@
|
|
|
|
|
// Create some global variables
|
|
|
|
|
var width, height, audioEl, textEl;
|
|
|
|
|
var width, height, audioEl, textEl, sampEl;
|
|
|
|
|
var mouseOverId;
|
|
|
|
|
var tooltip;
|
|
|
|
|
let currentTransform;
|
|
|
|
|
var links = [],
|
|
|
|
|
nodes = [],
|
|
|
|
@ -9,7 +11,45 @@ let lastK = 0;
|
|
|
|
|
let svg;
|
|
|
|
|
var zoom;
|
|
|
|
|
let simulation;
|
|
|
|
|
const ROOT = "https://hub.xpub.nl/chopchop/archive_non-tree/";
|
|
|
|
|
const ROOT = "https://hub.xpub.nl/chopchop/archive_non-tree/Active-Archive";
|
|
|
|
|
|
|
|
|
|
function getNameFromPath(path) {
|
|
|
|
|
return path.split("/")[path.split("/").length - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function wrap(text, width) {
|
|
|
|
|
|
|
|
|
|
text.each(function () {
|
|
|
|
|
var text = d3.select(this),
|
|
|
|
|
words = text.text().split(/\s+/).reverse(),
|
|
|
|
|
word,
|
|
|
|
|
line = [],
|
|
|
|
|
lineNumber = 0,
|
|
|
|
|
lineHeight = 1.1, // ems
|
|
|
|
|
x = text.attr("x"),
|
|
|
|
|
y = text.attr("y"),
|
|
|
|
|
dy = 0, //parseFloat(text.attr("dy")),
|
|
|
|
|
tspan = text.text(null)
|
|
|
|
|
.append("tspan")
|
|
|
|
|
.attr("x", x)
|
|
|
|
|
.attr("y", y)
|
|
|
|
|
.attr("dy", dy + "em");
|
|
|
|
|
while (word = words.pop()) {
|
|
|
|
|
line.push(word);
|
|
|
|
|
tspan.text(line.join(" "));
|
|
|
|
|
if (tspan.node().getComputedTextLength() > width) {
|
|
|
|
|
line.pop();
|
|
|
|
|
tspan.text(line.join(" "));
|
|
|
|
|
line = [word];
|
|
|
|
|
tspan = text.append("tspan")
|
|
|
|
|
.attr("x", x)
|
|
|
|
|
.attr("y", y)
|
|
|
|
|
.attr("dy", ++lineNumber * lineHeight + dy + "em")
|
|
|
|
|
.text(word);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseExifData(files) {
|
|
|
|
|
// loop trough all files in JSON and add each to the list of nodes
|
|
|
|
@ -33,6 +73,7 @@ function parseExifData(files) {
|
|
|
|
|
nodes.push({
|
|
|
|
|
id: path,
|
|
|
|
|
FileType: "directory",
|
|
|
|
|
FileName: getNameFromPath(path),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
@ -76,7 +117,11 @@ function parseExifData(files) {
|
|
|
|
|
|
|
|
|
|
function createGraph() {
|
|
|
|
|
// Create a scale to set the radius based on a file size (0 to 3 GB)
|
|
|
|
|
var rScale = d3.scaleLinear().domain([0, 30000000]).range([2, 15]);
|
|
|
|
|
var max = d3.max(nodes, function (d) {
|
|
|
|
|
return +d.FileSize;
|
|
|
|
|
});
|
|
|
|
|
console.log(max);
|
|
|
|
|
var rScale = d3.scaleLinear().domain([3, max]).range([2, 40]);
|
|
|
|
|
currentTransform = [width / 2, height / 2, height];
|
|
|
|
|
|
|
|
|
|
simulation = d3
|
|
|
|
@ -84,21 +129,18 @@ function createGraph() {
|
|
|
|
|
.force("center", d3.forceCenter(width / 2, height / 2))
|
|
|
|
|
.force(
|
|
|
|
|
"collision",
|
|
|
|
|
d3
|
|
|
|
|
.forceCollide()
|
|
|
|
|
// .radius((d) => rScale(d.size) + 1)
|
|
|
|
|
.iterations(3)
|
|
|
|
|
d3.forceCollide().radius((d) => rScale(d.FileSize) + 2)
|
|
|
|
|
)
|
|
|
|
|
.force(
|
|
|
|
|
"link",
|
|
|
|
|
d3
|
|
|
|
|
.forceLink(links)
|
|
|
|
|
.id((d) => d.id)
|
|
|
|
|
.strength((d) => 0.2)
|
|
|
|
|
// .distance((d) => (d.source.type === "directory" ? 80 : 20))
|
|
|
|
|
.strength((d) => 0.82)
|
|
|
|
|
.distance((d) => (d.source.FileType === "directory" ? 80 : 20))
|
|
|
|
|
)
|
|
|
|
|
.force("charge", d3.forceManyBody().strength(-1));
|
|
|
|
|
|
|
|
|
|
// .force("charge", d3.forceManyBody().strength(-1))
|
|
|
|
|
.force("charge", d3.forceManyBody());
|
|
|
|
|
// .alphaTarget(0.3); // stay hot
|
|
|
|
|
|
|
|
|
|
svg = d3
|
|
|
|
@ -108,15 +150,7 @@ function createGraph() {
|
|
|
|
|
|
|
|
|
|
const g = svg.append("g");
|
|
|
|
|
|
|
|
|
|
const labels = g
|
|
|
|
|
.append("g")
|
|
|
|
|
.selectAll("text")
|
|
|
|
|
.data(nodes)
|
|
|
|
|
.join("text")
|
|
|
|
|
.attr("font-size", (d) => (d.type === "directory" ? 12 : 3))
|
|
|
|
|
.text((d) => d.id)
|
|
|
|
|
.attr("cx", (d) => d.x)
|
|
|
|
|
.attr("cy", (d) => d.y);
|
|
|
|
|
tooltip = d3.select("body").append("div").attr("class", "tooltip");
|
|
|
|
|
|
|
|
|
|
const link = g
|
|
|
|
|
.append("g") //
|
|
|
|
@ -134,8 +168,7 @@ function createGraph() {
|
|
|
|
|
.selectAll("circle")
|
|
|
|
|
.data(nodes)
|
|
|
|
|
.join("circle")
|
|
|
|
|
.attr("r", (d) => rScale(d.size))
|
|
|
|
|
.attr("r", 3)
|
|
|
|
|
.attr("r", (d) => rScale(d.FileSize))
|
|
|
|
|
.attr("stroke", "black")
|
|
|
|
|
.attr("fill", (d) => {
|
|
|
|
|
if (d.FileType === "directory") {
|
|
|
|
@ -148,12 +181,58 @@ function createGraph() {
|
|
|
|
|
return "white";
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
// .attr("fill","rgba(255,255,255,.5)")
|
|
|
|
|
.attr("label", (d) => d.id)
|
|
|
|
|
.attr("path", (d) => d.id)
|
|
|
|
|
.attr("file-type", (d) => d.FileType)
|
|
|
|
|
.attr("group", (d) => d.group);
|
|
|
|
|
.attr("group", (d) => d.group)
|
|
|
|
|
.on("mouseenter", function (e, d) {
|
|
|
|
|
// if (d3.select(this).attr("fill") !== "black") {
|
|
|
|
|
// d3.select(this).attr("fill", "grey");
|
|
|
|
|
// }
|
|
|
|
|
tooltip.style("display", "block").html(tooltipContents(d));
|
|
|
|
|
})
|
|
|
|
|
.on("click", function () {
|
|
|
|
|
// d3.selectAll("circle").attr("fill", "rgba(255,255,255,.5)");
|
|
|
|
|
// d3.select(this).attr("fill", "black");
|
|
|
|
|
var path = this.getAttribute("path");
|
|
|
|
|
|
|
|
|
|
svg
|
|
|
|
|
.transition() //
|
|
|
|
|
.duration(300)
|
|
|
|
|
.ease(d3.easeCubic)
|
|
|
|
|
.call(zoom.translateTo, this.getAttribute("cx"), this.getAttribute("cy"));
|
|
|
|
|
|
|
|
|
|
// do things based on the file type
|
|
|
|
|
if (path.match(/\.(?:wav|mp3|flac)$/i)) {
|
|
|
|
|
playAudio(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (path.match(/\.(?:txt|png)$/i)) {
|
|
|
|
|
playText(path);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.on("mouseleave", function () {
|
|
|
|
|
if (d3.select(this).attr("fill") !== "black") {
|
|
|
|
|
d3.select(this).attr("fill", "rgba(255,255,255,.5)");
|
|
|
|
|
}
|
|
|
|
|
tooltip.style("display", "none");
|
|
|
|
|
})
|
|
|
|
|
.on("mousemove", function (event, d) {
|
|
|
|
|
if (!event) return;
|
|
|
|
|
tooltip.style("transform", `translate(${event.clientX}px, ${event.clientY}px)`);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const labels = g
|
|
|
|
|
.append("g")
|
|
|
|
|
.selectAll("text")
|
|
|
|
|
.data(nodes)
|
|
|
|
|
.join("text")
|
|
|
|
|
.attr("font-size", (d) => (d.FileType === "directory" ? 15 : 3))
|
|
|
|
|
.text((d) => d.FileName.replaceAll("-", " "))
|
|
|
|
|
.attr("cx", (d) => d.x)
|
|
|
|
|
.attr("cy", (d) => d.y)
|
|
|
|
|
.call(wrap, 50);
|
|
|
|
|
// Add a drag behavior.
|
|
|
|
|
node.call(
|
|
|
|
|
d3
|
|
|
|
@ -173,6 +252,7 @@ function createGraph() {
|
|
|
|
|
|
|
|
|
|
node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
|
|
|
|
|
labels.attr("x", (d) => d.x).attr("y", (d) => d.y);
|
|
|
|
|
// tooltip.attr("x", (d) => d.x).attr("y", (d) => d.y);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// add zoom behavior
|
|
|
|
@ -183,41 +263,9 @@ function createGraph() {
|
|
|
|
|
[width, height],
|
|
|
|
|
])
|
|
|
|
|
.scaleExtent([1, 100])
|
|
|
|
|
.on("zoom", zoomed);
|
|
|
|
|
.on("zoom", onZoom);
|
|
|
|
|
svg.call(zoom);
|
|
|
|
|
// add some mouse behaviors
|
|
|
|
|
svg
|
|
|
|
|
.selectAll("circle")
|
|
|
|
|
.on("mouseenter", function (e) {
|
|
|
|
|
if (d3.select(this).attr("fill") !== "black") {
|
|
|
|
|
d3.select(this).attr("fill", "grey");
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.on("click", function () {
|
|
|
|
|
d3.selectAll("circle").attr("fill", "rgba(255,255,255,.5)");
|
|
|
|
|
d3.select(this).attr("fill", "black");
|
|
|
|
|
var path = this.getAttribute("path");
|
|
|
|
|
|
|
|
|
|
svg
|
|
|
|
|
.transition() //
|
|
|
|
|
.duration(300)
|
|
|
|
|
.ease(d3.easeCubic)
|
|
|
|
|
.call(zoom.translateTo, this.getAttribute("cx"), this.getAttribute("cy"));
|
|
|
|
|
|
|
|
|
|
// do things based on the file type
|
|
|
|
|
if (path.match(/\.(?:wav|mp3|flac)$/i)) {
|
|
|
|
|
playAudio(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (path.match(/\.(?:txt|png)$/i)) {
|
|
|
|
|
playText(path);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.on("mouseleave", function () {
|
|
|
|
|
if (d3.select(this).attr("fill") !== "black") {
|
|
|
|
|
d3.select(this).attr("fill", "rgba(255,255,255,.5)");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
container.append(svg.node());
|
|
|
|
|
|
|
|
|
@ -242,22 +290,31 @@ function createGraph() {
|
|
|
|
|
event.subject.fy = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function zoomed(e) {
|
|
|
|
|
function onZoom(e) {
|
|
|
|
|
if (e.transform.k > 2 && lastK != e.transform.k) {
|
|
|
|
|
lastK = e.transform.k;
|
|
|
|
|
console.log("zoomed");
|
|
|
|
|
zoomLvl = Math.log2(e.transform.k);
|
|
|
|
|
globalNode.attr("stroke-width", 1 / zoomLvl);
|
|
|
|
|
link.attr("stroke-width", 1 / zoomLvl);
|
|
|
|
|
labels.attr("font-size", (d) => (d.type === "directory" ? 12 : 3) / zoomLvl);
|
|
|
|
|
labels.attr("font-size", (d) => (d.FileType === "directory" ? 20 : 3) / zoomLvl);
|
|
|
|
|
tooltip.attr("font-size", 3 / zoomLvl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.attr("transform", e.transform);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function tooltipContents(d) {
|
|
|
|
|
var string = "<ul>";
|
|
|
|
|
for (var prop in d) {
|
|
|
|
|
string += `<li><b>${prop}</b>: ${d[prop]}</li>`;
|
|
|
|
|
}
|
|
|
|
|
string += "</ul>";
|
|
|
|
|
return string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function playAudio(src) {
|
|
|
|
|
audioEl.src = src;
|
|
|
|
|
audioEl.src = ROOT + "/" + src;
|
|
|
|
|
audioEl.currentTime = 0;
|
|
|
|
|
audioEl.play();
|
|
|
|
|
}
|
|
|
|
@ -292,17 +349,10 @@ window.onload = function () {
|
|
|
|
|
|
|
|
|
|
audioEl = document.querySelector(".fn-audio");
|
|
|
|
|
textEl = document.querySelector(".fn-text");
|
|
|
|
|
sampEl = document.querySelector("samp");
|
|
|
|
|
|
|
|
|
|
parseExifData(dataset);
|
|
|
|
|
createGraph();
|
|
|
|
|
var filetypes = [];
|
|
|
|
|
nodes.forEach((node) => {
|
|
|
|
|
if (filetypes.indexOf(node.FileType) == -1) {
|
|
|
|
|
filetypes.push(node.FileType);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log(filetypes);
|
|
|
|
|
|
|
|
|
|
// clear loading screen
|
|
|
|
|
document.querySelector(".loading").style.display = "none";
|
|
|
|
|