njpma/app/static/js/ascroll.js

369 lines
12 KiB
JavaScript
Raw Normal View History

var cache = cache || {}
var layer = 'descriptions'
var baseURL = 'https://pad.ma'
var imageResolution = 480
2021-10-22 08:21:43 +00:00
var videoExtension
async function pandoraAPI(action, data) {
var url = baseURL + '/api/'
//var url = '/pandoraAPI/'
var key = JSON.stringify([action, data])
if (!cache[key]) {
var response = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
action: action,
data: data
})
})
cache[key] = await response.json()
}
return cache[key]
}
2021-10-22 08:21:43 +00:00
function setVideoSrc(video, src) {
var ext
if (!videoExtension) {
[
['video/mp4; codecs="avc1.42E01E, mp4a.40.2"', '.mp4'],
['video/webm; codecs="vp8, vorbis"', '.webm'],
].forEach(opt => {
if (videoExtension) { return }
if (video.canPlayType(opt[0]).replace('no', '')) {
videoExtension = opt[1]
}
})
}
video.src = src.replace('.webm', videoExtension)
}
2021-10-10 15:46:19 +00:00
function updatePlayer(video, frame, currentTime, out, src) {
var rect = frame.getBoundingClientRect();
video.style.opacity = 0
video.style.top = (rect.top + window.scrollY) + 'px'
video.style.display = 'block';
2021-10-10 15:06:43 +00:00
if (src) {
2021-10-22 08:21:43 +00:00
setVideoSrc(video, src)
2021-10-10 15:06:43 +00:00
}
//video.poster = frame.querySelector('img').src
2021-10-11 18:43:44 +00:00
var muted = video.muted
video.muted = true
video.currentTime = currentTime
2021-10-10 15:46:19 +00:00
video.dataset.in = currentTime
video.dataset.out = out
2021-10-11 09:28:51 +00:00
video._frame = frame
2021-10-11 18:43:44 +00:00
const show = event => {
video.style.opacity = 1
video.muted = muted
video.removeEventListener('seeked', show)
}
video.addEventListener('seeked', show)
video.play()
}
function isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */
);
}
function onVisibilityChange(el, callback) {
var old_visible;
return function () {
var visible = isElementInViewport(el);
if (visible != old_visible) {
old_visible = visible;
2021-10-12 09:38:25 +00:00
if (visible) {
el.classList.add('visible')
} else {
el.classList.remove('visible')
}
if (typeof callback == 'function') {
callback(visible);
}
}
}
}
2021-10-11 09:28:51 +00:00
function scrollTo(element) {
var delta = element.offsetTop - document.scrollingElement.scrollTop,
duration = 1000, t = 40, n = duration / t,
step = delta / n;
function scroll() {
if (document.scrollingElement.scrollTop + step > element.offsetTop) {
document.scrollingElement.scrollTop = element.offsetTop
n = 0
} else {
document.scrollingElement.scrollTop += step
}
n--
if (n) setTimeout(scroll, t)
}
scroll()
}
2021-10-10 15:46:19 +00:00
function timeupdate(event) {
if (event.target.dataset.out && event.target.dataset.in) {
var in_= parseFloat(event.target.dataset.in)
var out_= parseFloat(event.target.dataset.out)
if (event.target.currentTime >= out_) {
2021-10-11 09:28:51 +00:00
/*
var next
if (event.target._frame) {
next = event.target._frame.parentElement.nextSibling
if (next) {
next = next.querySelector('.frame')
}
}
if (next) {
scrollTo(next)
} else {
event.target.pause()
}
*/
2021-10-13 16:12:16 +00:00
if (config.pause) {
event.target.pause()
} else {
event.target.currentTime = in_
}
2021-10-10 15:46:19 +00:00
}
}
}
2021-10-11 13:50:19 +00:00
function formatInfo(config, ascroll) {
var info = document.createElement('div')
2021-10-11 13:50:19 +00:00
var h1 = document.createElement('h1')
h1.innerHTML = config.title
info.appendChild(h1)
2021-10-11 13:50:19 +00:00
var h2 = document.createElement('h2')
h2.innerHTML = config.byline
info.appendChild(h2)
2021-10-11 13:50:19 +00:00
var div = document.createElement('div')
div.classList.add('intro')
div.innerHTML = config.body
info.appendChild(div)
ascroll.appendChild(info)
return info
2021-10-11 13:50:19 +00:00
}
2021-10-11 18:43:44 +00:00
function showOverlay(event) {
2021-10-11 18:54:13 +00:00
event.stopPropagation()
event.preventDefault()
2021-10-11 18:43:44 +00:00
document.querySelectorAll('#video-overlay').forEach(element => element.remove())
var video = event.target
var rect = video.getBoundingClientRect();
var overlay = document.createElement('div')
overlay.id = 'video-overlay'
overlay.style.top = video.style.top
overlay.style.width = rect.width + 'px'
2021-10-13 15:35:28 +00:00
overlay.style.height = rect.height + 'px'
2021-10-11 18:43:44 +00:00
overlay.style.position = 'absolute'
overlay.style.display = 'flex'
overlay.style.alignItems = 'center'
overlay.style.justifyContent = 'center'
overlay.style.fontSize = '45px'
2021-10-13 15:35:28 +00:00
video.controls = false
var off = `🔇`
var on = `🔊`
2021-10-11 18:43:44 +00:00
if (video.muted) {
2021-10-13 15:35:28 +00:00
overlay.innerHTML = off
2021-10-11 18:43:44 +00:00
} else {
2021-10-13 15:35:28 +00:00
overlay.innerHTML = on
2021-10-11 18:43:44 +00:00
}
overlay.addEventListener('click', event=> {
video.muted = !video.muted
if (video.muted) {
2021-10-13 15:35:28 +00:00
overlay.innerHTML = off
2021-10-11 18:43:44 +00:00
} else {
2021-10-13 15:35:28 +00:00
overlay.innerHTML = on
2021-10-11 18:43:44 +00:00
}
})
2021-10-11 18:52:29 +00:00
var timeout = setTimeout(() => {
video.controls = false
2021-10-11 18:43:44 +00:00
overlay.remove()
2021-10-11 18:55:54 +00:00
}, 3000)
2021-10-11 18:52:29 +00:00
overlay.addEventListener('mousemove', event=> {
clearTimeout(timeout)
timeout = setTimeout(() => {
video.controls = false
overlay.remove()
}, 500)
2021-10-11 18:43:44 +00:00
})
video.parentElement.appendChild(overlay)
2021-10-11 18:52:29 +00:00
2021-10-11 18:43:44 +00:00
}
async function loadClips(annotations) {
var items = annotations.map(annotation => annotation.id.split('/')[0])
items = [...new Set(items)]
return pandoraAPI('findClips', {itemsQuery: {
conditions: [{key: 'id', operator: '&', value: items}]
}, range: [0, 10000], keys: [
'id', 'hue', 'saturation', 'lightness'
]}).then(response => {
var colors = {}
response.data.items.forEach(clip => {
colors[clip.id] = clip
})
var previous
annotations.forEach(annotation => {
var clipId = annotation.id.split('/')[0] + '/' + annotation.in.toFixed(3) + '-'+ annotation.out.toFixed(3)
annotation.color1 = colors[clipId]
if(previous) {
previous.color2 = annotation.color1
}
previous = annotation
})
2021-10-21 15:39:49 +00:00
if (annotations.length) {
annotations[annotations.length - 1].color2 = annotations[0].color1
}
return annotations
})
}
2021-10-10 15:06:43 +00:00
function loadItem(config) {
2021-10-11 09:28:51 +00:00
pandoraAPI('get', {id: config.item, keys: [
'id', 'title', 'layers', 'hue', 'saturation', 'lightness'
]}).then(response => {
//var color = `hsl(${response.data.hue}, ${response.data.saturation * 100}%, ${response.data.lightness * 100}%)`
var color = `hsl(${response.data.hue}, 70%, 50%)`
//document.body.style.background = color
2021-10-10 15:06:43 +00:00
var ascroll = document.querySelector('#ascroll')
var loaded = false
2021-10-10 15:06:43 +00:00
var video = document.createElement('video')
video.classList.add('player')
video.muted = true
2021-10-22 08:21:43 +00:00
setVideoSrc(video, `${baseURL}/${config.item}/480p.webm`)
2021-10-10 15:46:19 +00:00
video.addEventListener('timeupdate', timeupdate)
2021-10-11 18:43:44 +00:00
video.addEventListener('touchstart', showOverlay)
video.addEventListener('mouseover', showOverlay)
var box = document.createElement('div')
box.classList.add('vbox')
box.appendChild(video)
ascroll.appendChild(box)
2021-10-10 15:46:19 +00:00
var first
var info = formatInfo(config, ascroll)
var annotations = response.data.layers[config.layer].filter(annotation => {
2021-10-21 15:39:49 +00:00
return !(config.user && annotation.user != config.user)
})
loadClips(annotations).then(annotations => {
annotations.forEach(annotation => {
var div = document.createElement('div')
div.classList.add('annotation')
var color1 = `hsl(${annotation.color1.hue}, 70%, 75%)`
var color2 = `hsl(${annotation.color2.hue}, 70%, 75%)`
if (!first) {
first = annotation
info.style.background = color1
2021-10-12 09:38:25 +00:00
}
div.style.background = `linear-gradient(to bottom, ${color1}, ${color2})`;
div.innerHTML = `
<div class="frame"><img src="${baseURL}/${annotation.id.split('/')[0]}/${imageResolution}p${annotation.in}.jpg"></div>
<div class="text">${annotation.value}</div>
2021-10-10 15:06:43 +00:00
`
ascroll.appendChild(div)
var frame = div.querySelector('.frame')
document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) {
if (loaded && visible) {
updatePlayer(video, frame, annotation['in'], annotation['out'])
}
}))
})
loaded = true
let frame = ascroll.querySelector('.annotation .frame')
if (frame) {
updatePlayer(video, frame, first['in'], first['out'])
}
2021-10-10 15:06:43 +00:00
})
})
2021-10-10 15:06:43 +00:00
}
function loadEdit(config) {
pandoraAPI('getEdit', {id: config.edit, keys: []}).then(response => {
var ascroll = document.querySelector('#ascroll')
var loaded = false
var video = document.createElement('video')
video.classList.add('player')
video.muted = true
2021-10-10 15:46:19 +00:00
video.addEventListener('timeupdate', timeupdate)
2021-10-10 15:06:43 +00:00
ascroll.appendChild(video)
var first
var info = formatInfo(config, ascroll)
var previous
var annotations = []
2021-10-10 15:06:43 +00:00
response.data.clips.forEach(clip => {
2021-10-21 13:39:38 +00:00
clip.layers[config.layer].forEach(annotation => {
2021-10-10 15:06:43 +00:00
if (config.user && annotation.user != config.user) {
return
}
annotation.title = clip.title
annotations.push(annotation)
})
})
loadClips(annotations).then(annotations => {
annotations.forEach(annotation => {
var div = document.createElement('div')
div.classList.add('annotation')
var color1 = `hsl(${annotation.color1.hue}, 70%, 75%)`
var color2 = `hsl(${annotation.color2.hue}, 70%, 75%)`
2021-10-10 15:06:43 +00:00
if (!first) {
first = annotation
info.style.background = color1
2021-10-10 15:06:43 +00:00
}
div.style.background = `linear-gradient(to bottom, ${color1}, ${color2})`;
2021-10-10 15:06:43 +00:00
div.innerHTML = `
2021-10-10 15:25:17 +00:00
<div class="frame">
<figure>
<img src="${baseURL}/${annotation.id.split('/')[0]}/${imageResolution}p${annotation.in}.jpg">
<figcaption><a href="${baseURL}/${annotation.id}" target="_blank">${annotation.title}</a></figcaption>
2021-10-10 15:25:17 +00:00
</figure>
</div>
2021-10-10 15:06:43 +00:00
<div class="text">${annotation.value}</div>
`
ascroll.appendChild(div)
var frame = div.querySelector('.frame')
document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) {
var src = `${baseURL}/${annotation.id.split('/')[0]}/480p.webm`
2021-10-12 09:38:25 +00:00
if (loaded && visible) {
2021-10-10 15:46:19 +00:00
updatePlayer(video, frame, annotation['in'], annotation['out'], src)
2021-10-12 09:38:25 +00:00
}
2021-10-10 15:06:43 +00:00
}))
})
})
loaded = true
let frame = ascroll.querySelector('.annotation .frame')
if (frame) {
2021-10-10 15:25:17 +00:00
var src = `${baseURL}/${first.id.split('/')[0]}/480p.webm`
2021-10-10 15:46:19 +00:00
updatePlayer(video, frame, first['in'], first['out'], src)
2021-10-10 15:06:43 +00:00
}
})
2021-10-10 15:06:43 +00:00
}
2021-10-21 13:39:38 +00:00
config.layer = config.layer || layer
2021-10-10 15:06:43 +00:00
if (config.item) {
loadItem(config)
} else if (config.edit) {
loadEdit(config)
}