196 lines
6.7 KiB
HTML
Executable File
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>
|