mirror of
https://github.com/MiyakoYakota/search.0t.rocks.git
synced 2025-02-05 15:37:21 +00:00
298 lines
7.8 KiB
HTML
298 lines
7.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Cytoscape JS App</title>
|
|
<script src="https://unpkg.com/cytoscape@3.19.0/dist/cytoscape.min.js"></script>
|
|
<style>
|
|
#cy {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
}
|
|
|
|
/* Add this to your CSS file or inside a <style> tag */
|
|
#floating-menu {
|
|
position: fixed;
|
|
top: 10px;
|
|
right: 10px;
|
|
background-color: white;
|
|
border: 1px solid #ccc;
|
|
border-radius: 5px;
|
|
padding: 10px;
|
|
z-index: 999;
|
|
}
|
|
|
|
#floating-menu label,
|
|
#floating-menu select,
|
|
#floating-menu input {
|
|
margin-right: 5px;
|
|
}
|
|
</style>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/qtip2/3.0.3/jquery.qtip.min.css" />
|
|
<!-- Add these lines inside the <head> tag -->
|
|
<link rel="stylesheet" href="https://unpkg.com/tippy.js@6.3.1/dist/tippy.css" />
|
|
<!-- Add these lines before the app.js script -->
|
|
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
|
<script src="https://cytoscape.org/cytoscape.js-popper/cytoscape-popper.js"></script>
|
|
<script src="https://unpkg.com/tippy.js@6"></script>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
<!-- Add this inside the <body> tag -->
|
|
<div id="floating-menu">
|
|
<label for="layout-select">Graph Type:</label>
|
|
<select id="layout-select">
|
|
<option value="cose">Cose</option>
|
|
<option value="grid">Grid</option>
|
|
<option value="circle">Circle</option>
|
|
<option value="concentric">Concentric</option>
|
|
<option value="breadthfirst">Breadthfirst</option>
|
|
</select>
|
|
<label for="min-similarity">Minimum Similarity Score:</label>
|
|
<input type="number" id="min-similarity" min="0" value="10" />
|
|
<br />
|
|
<p>Statistics: The last query returned <span id="lastQueryCount">0</span> similar results, of which <span id="lastQueryShown">0</span> were shown. The lowest similarity score was <span id="lastQueryLowestScore">0</span>.</p>
|
|
</div>
|
|
|
|
<div id="cy"></div>
|
|
<script>
|
|
const tips = {}
|
|
|
|
const layoutSelect = document.getElementById('layout-select');
|
|
const minSimilarityInput = document.getElementById('min-similarity');
|
|
|
|
const lastQueryCount = document.getElementById("lastQueryCount")
|
|
const lastQueryShownCount = document.getElementById("lastQueryShown")
|
|
const lastQueryShownLowestScore = document.getElementById("lastQueryLowestScore")
|
|
|
|
|
|
// Set default values
|
|
let selectedLayout = 'circle';
|
|
let minSimilarity = 10;
|
|
|
|
|
|
// Initialize Cytoscape instance
|
|
const cy = cytoscape({
|
|
container: document.getElementById('cy'),
|
|
elements: [],
|
|
style: [
|
|
{
|
|
selector: 'node',
|
|
style: {
|
|
'background-color': '#0074D9',
|
|
'label': 'data(id)'
|
|
}
|
|
},
|
|
{
|
|
selector: 'edge',
|
|
style: {
|
|
'width': 3,
|
|
'line-color': '#7FDBFF',
|
|
'curve-style': 'bezier'
|
|
}
|
|
}
|
|
],
|
|
layout: {
|
|
name: selectedLayout
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Function to apply the selected layout
|
|
function applyLayout() {
|
|
cy.layout({ name: selectedLayout }).run();
|
|
}
|
|
|
|
// Event listener for layout selection
|
|
layoutSelect.addEventListener('change', (event) => {
|
|
selectedLayout = event.target.value;
|
|
applyLayout();
|
|
});
|
|
|
|
// Event listener for minimum similarity input
|
|
minSimilarityInput.addEventListener('input', (event) => {
|
|
|
|
minSimilarity = parseInt(event.target.value);
|
|
console.log("New minimum similarity: " + minSimilarity)
|
|
});
|
|
|
|
// Apply the default layout when the graph is loaded
|
|
applyLayout();
|
|
|
|
|
|
var makeTippy = function (ele, text) {
|
|
var ref = ele.popperRef();
|
|
|
|
// Since tippy constructor requires DOM element/elements, create a placeholder
|
|
var dummyDomEle = document.createElement('div');
|
|
|
|
var tip = tippy(dummyDomEle, {
|
|
getReferenceClientRect: ref.getBoundingClientRect,
|
|
trigger: 'manual', // mandatory
|
|
// dom element inside the tippy:
|
|
content: function () { // function can be better for performance
|
|
var div = document.createElement('div');
|
|
|
|
div.innerHTML = text;
|
|
|
|
return div;
|
|
},
|
|
// your own preferences:
|
|
arrow: true,
|
|
placement: 'bottom',
|
|
hideOnClick: false,
|
|
sticky: "reference",
|
|
|
|
// if interactive:
|
|
interactive: true,
|
|
appendTo: document.body // or append dummyDomEle to document.body
|
|
});
|
|
|
|
return tip;
|
|
};
|
|
|
|
function createDocSummary(doc) {
|
|
const blacklist = ['id', '_version_', 'line', 'notes']
|
|
const summary = []
|
|
|
|
for (const key in doc) {
|
|
if (!blacklist.includes(key)) {
|
|
summary.push(`${key}: ${doc[key]}<br />`)
|
|
}
|
|
}
|
|
|
|
return summary.join('\n')
|
|
}
|
|
|
|
|
|
|
|
// Function to add new nodes (modified to include tooltip creation)
|
|
function addDocument(record) {
|
|
const id = record.id
|
|
|
|
let newNode = null;
|
|
|
|
try {
|
|
newNode = cy.add({
|
|
group: 'nodes',
|
|
data: { id },
|
|
});
|
|
|
|
const tippy = makeTippy(newNode, createDocSummary(record));
|
|
|
|
tips[id] = tippy
|
|
|
|
} catch (e) {
|
|
console.log(e)
|
|
}
|
|
|
|
|
|
cy.layout({
|
|
name: selectedLayout,
|
|
minNodeSpacing: 200
|
|
}).run();
|
|
|
|
return newNode
|
|
|
|
// cy.layout({ name: 'grid', rows: 1 }).run();
|
|
}
|
|
|
|
|
|
// Function to create links between nodes
|
|
function createLink(source, target) {
|
|
cy.add({
|
|
group: 'edges',
|
|
data: {
|
|
id: `${source}-${target}`,
|
|
source,
|
|
target
|
|
}
|
|
});
|
|
}
|
|
|
|
// Event handler for node click
|
|
cy.on('tap', 'node', async function (event) {
|
|
const nodeId = event.target.data('id');
|
|
try {
|
|
const response = await fetch(`/documents/by_id/${nodeId}?wt=json&moreLikeThis=true`);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
lastQueryCount.innerText = data.related.length
|
|
|
|
let countShown = 0
|
|
|
|
let lowestScore = 1000000;
|
|
|
|
data.related.forEach((record) => {
|
|
if (record['similarity score'] < lowestScore) {
|
|
lowestScore = record['similarity score']
|
|
}
|
|
if (record['similarity score'] >= minSimilarity) {
|
|
addDocument(record)
|
|
createLink(nodeId, record.id)
|
|
countShown += 1
|
|
console.log(record['similarity score'])
|
|
}
|
|
})
|
|
if (lowestScore === 1000000) {
|
|
lastQueryShownLowestScore.innerText = 0
|
|
} else {
|
|
lastQueryShownLowestScore.innerText = lowestScore
|
|
}
|
|
} else {
|
|
console.error('Error fetching data from API:', response.status, response.statusText);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching data from API:', error);
|
|
}
|
|
});
|
|
|
|
cy.on('mouseover', 'node', function (event) {
|
|
tips[event.target.id()].show()
|
|
});
|
|
|
|
cy.on('mouseout', 'node', function (event) {
|
|
tips[event.target.id()].hide()
|
|
});
|
|
|
|
|
|
(async () => {
|
|
// Get URL parameters
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const nodeId = urlParams.get('id');
|
|
|
|
if (nodeId) {
|
|
const response = await fetch(`/documents/by_id/${nodeId}?wt=json`);
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
if (nodeId) {
|
|
const initialNode = addDocument(data.record);
|
|
const initialNodeTip = makeTippy(initialNode, "Click Me!");
|
|
initialNodeTip.show()
|
|
|
|
setTimeout(() => {
|
|
initialNodeTip.hide()
|
|
}, 7000)
|
|
}
|
|
}
|
|
else {
|
|
alert("Error fetching from API")
|
|
}
|
|
}
|
|
})()
|
|
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html> |