var layer = 'keywords' var imageResolution = 480 var videoExtension 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] } }) } src = src.replace('.webm', videoExtension) if (src != video.src) { video.src = src } } function resize() { var video = document.querySelector('video') if (video && video._frame) { var rect = video._frame.getBoundingClientRect(); video.style.top = (rect.top + window.scrollY) + 'px' } } 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'; if (src) { setVideoSrc(video, src) } //video.poster = frame.querySelector('img').src var muted = video.muted // video.muted = true video.currentTime = currentTime video.dataset.in = currentTime video.dataset.out = out video._frame = frame 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() */ Math.floor(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; if (visible) { el.classList.add('visible') } else { el.classList.remove('visible') } if (typeof callback == 'function') { callback(visible); } } } } 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() } function timeupdate(event) { var video = event.target var now = video.currentTime document.querySelectorAll('#player .annotation').forEach(annotation => { var display if ( parseFloat(annotation.dataset['in']) < now && parseFloat(annotation.dataset['out']) > now ) { display = 'block' } else { display = 'none' } if (annotation.style.display != display) { annotation.style.display = display } }) 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_) { /* 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() } */ if (config.pause) { event.target.pause() } else { event.target.currentTime = in_ } } } } function formatInfo(config, player) { var info = document.createElement('div') info.classList.add('info') var folder = document.createElement('a') folder.href = '..' folder.innerHTML = config.folder var h1 = document.createElement('h1') h1.appendChild(folder) var title = document.createElement('span') title.innerHTML = ' - ' + config.title h1.appendChild(title) info.appendChild(h1) var folder = document.createElement('a') folder.href = '..' folder.innerHTML = config.folder var h1 = document.createElement('h1') h1.appendChild(folder) var title = document.createElement('span') title.innerHTML = ' - ' + config.title h1.appendChild(title) var t = document.querySelector('.topnav .item-title') t.appendChild(h1) player.appendChild(info) return info } function renderAnnotation(config, video, player, annotation) { var div = document.createElement('div') div.classList.add('annotation') annotation.value = annotation.value.replace(/src="\//g, `src="${pandoraURL}/`).replace(/href="\//g, `href="${pandoraURL}/`) div.dataset['in'] = annotation['in'] div.dataset['out'] = annotation['out'] div.innerHTML = `
${annotation.value}
` player.appendChild(div) } function renderPlayer(config) { var player = document.querySelector('#ascroll') player.id = 'player' config.loaded = false var video = document.createElement('video') video.classList.add('player') video.playsinline = true video.setAttribute('playsinline', 'playsinline') video.setAttribute('webkit-playsinline', 'webkit-playsinline') video.WebKitPlaysInline = true video.muted = false video.controls = true if (config.item) { setVideoSrc(video, `${streamPrefix}/${config.item}/480p.webm`) video.poster = `${streamPrefix}/${config.item}/480p.jpg` } video.addEventListener('timeupdate', timeupdate) var box = document.createElement('div') box.classList.add('vbox') box.appendChild(video) config.info = formatInfo(config, player) var stage = document.createElement('div') stage.classList.add('stage') stage.appendChild(box) var div = document.createElement('div') div.classList.add('annotations') config.annotations.forEach(annotation => { renderAnnotation(config, video, div, annotation) }) stage.appendChild(div) player.appendChild(stage) config.loaded = true } 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 }) if (annotations.length) { annotations[annotations.length - 1].color2 = annotations[0].color1 } return annotations }) } function loadAnnotations(config) { if (config.item) { pandoraAPI('get', {id: config.item, keys: [ 'title', 'date', 'country', 'featuring', 'layers', 'folder' ]}).then(response => { ['title', 'date', 'country', 'featuring', 'folder'].forEach(key => { config[key] = response.data[key] }) var annotations = [] Object.keys(response.data.layers).forEach(layer => { response.data.layers[layer].forEach(annotation => { if (!(config.user && annotation.user != config.user)) { annotations.push(annotation) } }) }) loadClips(annotations).then(annotations => { config.annotations = annotations.filter(annotation => { if (config.only_e) { if (annotation.value.slice(0, 2) == 'E:') { annotation.value = annotation.value.slice(2).trim() return true } else { return false } } else { return annotation.value.slice(0, 2) != 'E:' } }) renderPlayer(config) }) }) } else { var cited = {} pandoraAPI('getEdit', {id: config.edit, keys: []}).then(response => { var annotations = [] response.data.clips.forEach(clip => { cited[clip.item] = { title: clip.title, director: clip.director, id: clip.item } clip.layers[config.layer].forEach(annotation => { if (config.user && annotation.user != config.user) { return } ;['title', 'director', 'date'].forEach(key => { annotation[key] = clip[key] }) if (config.only_e) { if (annotation.value.slice(0, 2) == 'E:') { annotation.value = annotation.value.slice(2).trim() annotations.push(annotation) } } else { if (annotation.value.slice(0, 2) != 'E:') { annotations.push(annotation) } } }) }) loadClips(annotations).then(annotations => { config.annotations = annotations config.cited = Object.values(cited) renderPlayer(config) }) }) } } config.layer = config.layer || layer loadAnnotations(config) window.addEventListener('resize', resize, false);