Files
spasca-fot-sloan-q4/data/ht25_annotatedbibliography_spatial_json_viewer_example.html
Utopiah cf8d3138d5 files
2025-12-16 09:52:17 +01:00

196 lines
6.7 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://fabien.benetou.fr/pub/home/future_of_text_demo/engine/dependencies/webdav.js"></script>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js';
window.THREE = THREE
</script>
</head>
<body>
<div id=search>
Content search : <input onkeyup="search()" id=fulltextsearch></input>
<button onclick="fulltextsearch.value=''">clear</button>
<br>
minimum timestamp : <input onkeyup="searchByDate()" id=timesstampsearch></input>
<button onclick="timesstampsearch.value=''">clear</button>
<br>
<span id=found style=visibility:hidden>(<span id=foundnumber></span> item(s) found)</span>
</div>
<div id=content></div>
<script>
// insert screenshots (could probably do the same way, i.e. filereader then webdav upload
const webdavURL = "https://webdav.benetou.fr";
const subdirWebDAV = "/fotsave/fot_sloan_companion_public/"
var webdavClient = window.WebDAV.createClient(webdavURL)
// const eventSourceConverted = new EventSource( `https://ntfy.benetou.fr/convertedwebdav/sse` )
// to use for live updates
const prefix = 'q3_demo_ht25_annotatedbibliography_spatial_savingDataDemo_q3_annotatedbibliography_spatial_ht25_'
const extension ='.json'
const embedURL = '/index.html?username=q3_demo_ht25_annotatedbibliography_spatial&remoteStorageURL='
// TODO
// distance based grouping
// e.g. if note within X distance of highlight, mention it somehow
const distanceThreshold = .3
async function getContent(){
let rootEl = document.getElementById("content")
const contents = await webdavClient.getDirectoryContents(subdirWebDAV);
// consider instead search https://github.com/perry-mitchell/webdav-client#search
contents.filter(f => f.basename.startsWith(prefix) && f.basename.endsWith(extension))
.sort( (a,b) => new Date(a.lastmod).getTime() < new Date(b.lastmod).getTime() ) // newest first
// could also filter beyond a specific date to avoid using testing set
.map(a => {
fetch(a.basename).then( r => r.json() ).then( r => {
if (r.notes.length > 0 || r.highlights.length > 0) {
let div = document.createElement("div")
div.classList.add('item')
div.data = {}
div.data.basename = a.basename
div.data.json = r
div.data.date = Number(a.basename.replace(prefix,'').replace(extension,'') )
rootEl.appendChild(div)
let h3 = document.createElement("h3")
h3.innerHTML = 'Save ' + a.basename.replace(prefix,'').replace(extension,'') + ' '
div.appendChild(h3)
let link = document.createElement("a")
link.href = a.basename
link.innerText = '(JSON)'
link.target = "_blank"
h3.appendChild(link)
let embedLink = document.createElement("a")
embedLink.href = embedURL + a.basename
embedLink.innerText = '(embed)'
embedLink.target = "_blank"
h3.appendChild(embedLink)
let h4 = document.createElement("h4")
h4.innerText = "highlights"
div.appendChild(h4)
let ul = document.createElement("ul")
div.appendChild(ul)
let highlightsEl = []
r.highlights.filter( h => h.value.length>0 ).map( h => {
let li = document.createElement("li")
li.innerText = h.value
ul.appendChild(li)
ul.title = "position: " + coordToString(h.position) + ", rotation:" + coordToString(h.rotation)
li.classList.add('searchableitem')
highlightsEl.push(li)
li.data = h
})
let nh4 = document.createElement("h4")
nh4.innerText = "notes"
div.appendChild(nh4)
let nul = document.createElement("ul")
div.appendChild(nul)
r.notes.filter( h => h.value.length>0 ).map( h => { // skipping empty notes
let li = document.createElement("li")
li.innerText = h.value
li.classList.add('searchableitem')
li.title = "position: " + coordToString(h.position) + ", rotation:" + coordToString(h.rotation)
nul.appendChild(li)
li.data = h
let v3 = new THREE.Vector3().copy( h.position )
r.highlights.map( hv => {
let v3_h = new THREE.Vector3().copy( hv.position )
if ( v3.distanceTo(v3_h) < distanceThreshold ){
console.log( h.value, 'close to', hv.value )
li.innerText += ' (close to "' + hv.value + '")'
}
})
tagging( li, highlightsEl )
})
let hr = document.createElement("hr")
div.appendChild(hr)
}
})
})
}
getContent()
function search(){
let searchTerm = fulltextsearch.value
console.log( searchTerm )
Array.from( document.querySelectorAll(".searchableitem"))
.map( e => e.parentElement.parentElement.style.opacity = 0.1 )
let founds = Array.from( document.querySelectorAll(".searchableitem"))
//.filter( e => e.innerText.toLowerCase().includes(searchTerm.toLowerCase()) )
.filter( e => e.innerHTML.toLowerCase().includes(searchTerm.toLowerCase()) ) // somehow .innerText gest empty
.map( e => e.parentElement.parentElement.style.opacity = 1 )
// does not work for some notes...
// e.g did HI work (at some point?!) but not BGN
// maybe because it's not a direct successor
foundnumber.innerText = founds.length
found.style.visibility="visible"
}
function searchByDate(){
let searchDate = Number(timesstampsearch.value)
console.log( searchDate)
let searchTerm = fulltextsearch.value
Array.from( document.querySelectorAll(".searchableitem"))
.map( e => e.parentElement.parentElement.style.opacity = 0.1 )
let founds = Array.from( document.querySelectorAll(".searchableitem"))
.filter( e => e.innerHTML.toLowerCase().includes(searchTerm.toLowerCase()) ) // somehow .innerText gest empty
.filter( e => Number(e.parentElement.parentElement.data.date) > searchDate)
.map( e => e.parentElement.parentElement.style.opacity = 1 )
foundnumber.innerText = founds.length
found.style.visibility="visible"
}
function coordToString(vec3){
return "x:" + vec3.x.toFixed(3) +", y:" + vec3.y.toFixed(3) +", z:" + vec3.z.toFixed(3)
}
function tagging(tag, highlightsEl){
if (!tag.data.value) return
if (!tag.data.value.toLowerCase().startsWith("tag")) return
console.log(tag.data.value, 'tag test')
// comes from notes...
let v3 = new THREE.Vector3().copy( tag.data.position )
v3.y = 0 // ignoring vertical position
highlightsEl.map( hv => {
let v3_h = new THREE.Vector3().copy( hv.data.position )
v3_h.y = 0 // ignoring vertical position, could refine that to only being below the tag
if ( v3.distanceTo(v3_h) < distanceThreshold*2 ){
console.log( hv.data.value, ' (tagged by "' + tag.data.value + '")' )
hv.title += ' (tagged "' + tag.data.value.replace('TAG ','') + '")'
// seems to do so multiple times
}
})
}
</script>
<div id=comments>
<br>
<br>
</div>
</body>
</html>