Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
32a9720e6e | ||
8794207e1d | |||
0a4825e14b | |||
27e8d44db0 | |||
961678fbca | |||
78c13a285b | |||
67cce94580 | |||
|
3284c5ca81 | ||
|
2cc261b604 | ||
be68a2429b | |||
16d7ffa19a | |||
|
ba2e8e4d75 | ||
18cdcee3c1 | |||
|
79c00627a3 |
|
@ -57,4 +57,5 @@ body {
|
|||
|
||||
.audio iframe {
|
||||
height: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
|
|
@ -1,40 +1,253 @@
|
|||
class Slide {
|
||||
constructor(slide, idx) {
|
||||
this.isAudioLoaded = this.isVideoLoaded = false
|
||||
this.el = slide
|
||||
this.idx = idx
|
||||
this.duration = slide.dataset.duration
|
||||
const video = slide.querySelector('.video')
|
||||
if (video) {
|
||||
this.video = load_urls(video.dataset)
|
||||
if (this.video.length > 1 || this.video[0].includes('document')) {
|
||||
this.isDocument = true
|
||||
}
|
||||
this.videoVolume = video.dataset.volume ? parseFloat(video.dataset.volume) : 1
|
||||
this.videoContainer = video
|
||||
if (this.video.length > 1) {
|
||||
this.zooms = this.video.map(url => url.split('/').pop().split('#')[0].split(',').map(a => Math.round(a)))
|
||||
}
|
||||
}
|
||||
const audio = slide.querySelector('.audio')
|
||||
if (audio) {
|
||||
this.audio = audio.dataset.url // audio does not need to be an array
|
||||
this.audioVolume = audio.dataset.volume ? parseFloat(audio.dataset.volume) : 1
|
||||
this.audioContainer = audio
|
||||
this.audioContinue = !!audio.dataset.continue
|
||||
}
|
||||
this.initEmbeds()
|
||||
return this
|
||||
}
|
||||
|
||||
initEmbeds() {
|
||||
if (this.video) {
|
||||
const videoEmbed = this.videoEmbed = new PandoraEmbed({
|
||||
id: 'slide-' + this.idx,
|
||||
url: this.video[0],
|
||||
container: this.videoContainer
|
||||
})
|
||||
|
||||
videoEmbed.on('init', () => {
|
||||
if (this.isDocument) {
|
||||
this.isVideoLoaded = true
|
||||
}
|
||||
})
|
||||
|
||||
videoEmbed.on('loaded', () => {
|
||||
console.log('loaded called on embed')
|
||||
this.isVideoLoaded = true
|
||||
})
|
||||
|
||||
videoEmbed.on('playing', (positionData) => {
|
||||
if (!this.videoStart) {
|
||||
this.videoStart = positionData.position
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (this.audio) {
|
||||
const audioEmbed = this.audioEmbed = new PandoraEmbed({
|
||||
id: 'audio-' + this.idx,
|
||||
url: this.audio,
|
||||
container: this.audioContainer
|
||||
})
|
||||
|
||||
audioEmbed.on('loaded', () => {
|
||||
console.log('loaded called on audio embed')
|
||||
this.isAudioLoaded = true
|
||||
})
|
||||
|
||||
audioEmbed.on('playing', (positionData) => {
|
||||
if (!this.audioStart) {
|
||||
this.audioStart = positionData.position
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
isReady() {
|
||||
console.log('isReady called');
|
||||
if (this.video && this.audio) {
|
||||
return this.isVideoLoaded && this.isAudioLoaded
|
||||
} else if (this.video) {
|
||||
return this.isVideoLoaded
|
||||
} else if (this.audio) {
|
||||
return this.isAudioLoaded
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sendToBack() {
|
||||
this.el.style.zIndex = 0
|
||||
return this
|
||||
}
|
||||
|
||||
bringToFront() {
|
||||
this.el.style.zIndex = 10
|
||||
return this
|
||||
}
|
||||
|
||||
start() {
|
||||
console.log('called start', this.isReady())
|
||||
if (this.zooms) {
|
||||
this.startZoom()
|
||||
} else if (this.video && this.video.length) {
|
||||
this.startVideo()
|
||||
}
|
||||
if (this.audioContainer) { // if there is an audio container, we always stop current audio
|
||||
reset_active_audio()
|
||||
}
|
||||
if (this.audio) {
|
||||
this.startAudio()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.zooms) {
|
||||
this.resetZoom()
|
||||
} else if (this.video && this.video.length) {
|
||||
this.resetVideo()
|
||||
}
|
||||
if (this.audio && !this.audioContinue) {
|
||||
this.resetAudio()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
startZoom() {
|
||||
for (var i=1; i<this.zooms.length; i++) {
|
||||
this.zoomTimeouts = [];
|
||||
((j) => {
|
||||
this.zoomTimeouts.push(setTimeout(() => {
|
||||
this.videoEmbed.postMessage('options', {
|
||||
'area': this.zooms[j]
|
||||
})
|
||||
}, Math.round(1000 * (this.duration / this.zooms.length) * j)))
|
||||
})(i);
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
startVideo() {
|
||||
this.videoEmbed.postMessage('options', {
|
||||
'paused': false,
|
||||
'volume': this.videoVolume
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
startAudio() {
|
||||
this.audioEmbed.postMessage('options', {
|
||||
'paused': false,
|
||||
'volume': this.audioVolume
|
||||
})
|
||||
activeAudio = this
|
||||
return this
|
||||
}
|
||||
|
||||
resetZoom() {
|
||||
this.zoomTimeouts.forEach(timeout => clearTimeout(timeout))
|
||||
this.videoEmbed.postMessage('options', {
|
||||
'area': this.zooms[0]
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
resetVideo() {
|
||||
this.videoEmbed.postMessage('options', {
|
||||
'paused': true,
|
||||
'position': this.start || 0
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
resetAudio() {
|
||||
this.audioEmbed.postMessage('options', {
|
||||
'paused': true,
|
||||
'position': 0 //FIXME: figure correct audio reset
|
||||
})
|
||||
activeAudio = null;
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
var slides = [],
|
||||
slideData = [],
|
||||
current = 0,
|
||||
activeAudio,
|
||||
timeout;
|
||||
|
||||
let textbUrl = new URLSearchParams(window.location.search).get('textb') || 'https://textb.org/r/housingplaylist2/'
|
||||
|
||||
// use raw version of page
|
||||
textbUrl = textbUrl.replace('/t/', '/r/')
|
||||
if (!textbUrl.endsWith('/')) textbUrl += '/'
|
||||
|
||||
fetch(textbUrl)
|
||||
.then(response => response.text())
|
||||
.then(loadYaml)
|
||||
.then(init)
|
||||
.catch(err => {
|
||||
console.log('error', err)
|
||||
alert('error loading YAML')
|
||||
})
|
||||
|
||||
|
||||
function loadYaml(txt) {
|
||||
const html = txt.split('\n\n')
|
||||
.filter(chunk => chunk.trim())
|
||||
.map(chunk => {
|
||||
const obj = jsyaml.load(chunk)
|
||||
return obj
|
||||
})
|
||||
.map(jsonToHTML)
|
||||
.join('')
|
||||
document.body.innerHTML = `
|
||||
<div class="base"></div>
|
||||
${html}
|
||||
`
|
||||
return true
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
function init() {
|
||||
document.querySelectorAll('.slide').forEach(function(slide) {
|
||||
slides.push(slide)
|
||||
var data = slideData[slides.length - 1] = load_slide(slide);
|
||||
data.idx = slides.length - 1;
|
||||
if (data.video && data.video.length) {
|
||||
data.embed = init_embed(data)
|
||||
}
|
||||
if (data.audio) {
|
||||
data.audioEmbed = init_audio_embed(data)
|
||||
}
|
||||
document.querySelectorAll('.slide').forEach(function(slide, idx) {
|
||||
slides.push(new Slide(slide, idx))
|
||||
})
|
||||
go(0)
|
||||
}
|
||||
|
||||
function go(idx) {
|
||||
if (!slides[idx].isReady()) {
|
||||
console.log('slide not ready');
|
||||
return setTimeout(() => {
|
||||
go(idx)
|
||||
}, 250)
|
||||
}
|
||||
var old = current
|
||||
slides[current].style.zIndex = 0
|
||||
slides[idx].style.zIndex = 10
|
||||
|
||||
slides[current].sendToBack()
|
||||
slides[idx].bringToFront()
|
||||
current = idx
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
timeout = null
|
||||
}
|
||||
timeout = setTimeout(next, Math.round(slides[current].dataset.duration) * 1000)
|
||||
timeout = setTimeout(next, Math.round(slides[current].duration) * 1000)
|
||||
|
||||
stop(old)
|
||||
start(current)
|
||||
slides[old].stop()
|
||||
slides[current].start()
|
||||
}
|
||||
|
||||
function next() {
|
||||
|
@ -51,34 +264,6 @@ function previous() {
|
|||
go(idx)
|
||||
}
|
||||
|
||||
function start(idx) {
|
||||
var data = slideData[idx]
|
||||
console.log('start', current, data)
|
||||
if (data.zooms) {
|
||||
start_zoom(data)
|
||||
} else if (data.video && data.video.length) {
|
||||
start_video(data)
|
||||
}
|
||||
if (data.audioContainer) { // if there is an audio container, we always stop current audio
|
||||
reset_active_audio();
|
||||
}
|
||||
if (data.audio) {
|
||||
start_audio(data)
|
||||
}
|
||||
}
|
||||
|
||||
function stop(idx) {
|
||||
var data = slideData[idx]
|
||||
console.log(current, data)
|
||||
if (data.zooms) {
|
||||
reset_zoom(data)
|
||||
} else if (data.video && data.video.length) {
|
||||
reset_video(data)
|
||||
}
|
||||
if (data.audio && !data.audioContinue) {
|
||||
reset_audio(data)
|
||||
}
|
||||
}
|
||||
|
||||
function load_urls(dataset) {
|
||||
var urls = [], idx = 0
|
||||
|
@ -89,54 +274,6 @@ function load_urls(dataset) {
|
|||
return urls
|
||||
}
|
||||
|
||||
function load_slide(slide) {
|
||||
var data = {}
|
||||
data.duration = slide.dataset.duration
|
||||
var video = slide.querySelector('.video')
|
||||
if (video) {
|
||||
data.video = load_urls(video.dataset)
|
||||
data.container = video
|
||||
|
||||
// assumes documents if length > 1
|
||||
if (data.video.length > 1) {
|
||||
data.zooms = data.video.map(url => url.split('/').pop().split('#')[0].split(',').map(a => Math.round(a)))
|
||||
}
|
||||
}
|
||||
var audio = slide.querySelector('.audio')
|
||||
if (audio) {
|
||||
data.audio = audio.dataset.url // audio does not need to be an array
|
||||
data.audioContainer = audio
|
||||
console.log('continue', audio.dataset.continue)
|
||||
data.audioContinue = !!audio.dataset.continue
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function init_embed(data) {
|
||||
var embed = window.currentEmbed = new PandoraEmbed({
|
||||
id: 'slide-' + data.idx,
|
||||
url: data.video[0],
|
||||
container: data.container
|
||||
});
|
||||
embed.on('init', function(data) {
|
||||
|
||||
});
|
||||
embed.on('playing', function(positionData) {
|
||||
if (!slideData[data.idx].start) {
|
||||
slideData[data.idx].start = positionData.position;
|
||||
}
|
||||
});
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
function init_audio_embed(data) {
|
||||
return new PandoraEmbed({
|
||||
id: 'slide-audio-' + data.idx,
|
||||
url: data.audio,
|
||||
container: data.audioContainer
|
||||
})
|
||||
}
|
||||
|
||||
window.addEventListener('blur', function(){
|
||||
setTimeout(function(){
|
||||
|
@ -148,60 +285,9 @@ window.addEventListener('blur', function(){
|
|||
}, false);
|
||||
|
||||
|
||||
function reset_zoom(data) {
|
||||
data.zoomTimeouts.forEach(timeout => clearTimeout(timeout))
|
||||
data.embed.postMessage('options', {
|
||||
'area': data.zooms[0]
|
||||
})
|
||||
}
|
||||
|
||||
function start_zoom(data) {
|
||||
// console.log('start zoon', Math.round(1000 * data.duration / 2))
|
||||
|
||||
for (var i=1; i<data.zooms.length; i++) {
|
||||
data.zoomTimeouts = [];
|
||||
(function(j) {
|
||||
data.zoomTimeouts.push(setTimeout(function() {
|
||||
data.embed.postMessage('options', {
|
||||
'area': data.zooms[j]
|
||||
})
|
||||
}, Math.round(1000 * (data.duration / data.zooms.length) * j)))
|
||||
})(i)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function start_video(data) {
|
||||
data.embed.postMessage('options', {
|
||||
'paused': false
|
||||
})
|
||||
}
|
||||
|
||||
function start_audio(data) {
|
||||
data.audioEmbed.postMessage('options', {
|
||||
'paused': false
|
||||
})
|
||||
activeAudio = data
|
||||
}
|
||||
|
||||
function reset_video(data) {
|
||||
data.embed.postMessage('options', {
|
||||
'paused': true,
|
||||
'position': data.start || 0
|
||||
})
|
||||
}
|
||||
|
||||
function reset_audio(data) {
|
||||
data.audioEmbed.postMessage('options', {
|
||||
'paused': true,
|
||||
'position': 0 //FIXME: figure correct audio reset
|
||||
})
|
||||
activeAudio = null;
|
||||
}
|
||||
|
||||
function reset_active_audio() {
|
||||
if (activeAudio) {
|
||||
reset_audio(activeAudio)
|
||||
activeAudio.resetAudio()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +1,9 @@
|
|||
<meta charset="utf-8">
|
||||
<script src="../lib/PandoraEmbed.js"></script>
|
||||
<link type="text/css" href="example.css" rel="stylesheet" />
|
||||
<div class="base">
|
||||
</div>
|
||||
<div class="slide" data-duration="10">
|
||||
<div class="title">FROM JANTA COLONY TO JANTA COLONY</div>
|
||||
</div>
|
||||
<div class="slide" data-duration="10">
|
||||
<div class="title">
|
||||
At first, three stories from the year 1950 in Bombay
|
||||
</div>
|
||||
<div class="audio" data-continue="true" data-url="https://pad.ma/BVF/editor/F#embed"></div>
|
||||
</div>
|
||||
<div class="slide" data-duration="10">
|
||||
<div class="title">
|
||||
ONE
|
||||
</div>
|
||||
</div>
|
||||
<div class="slide" data-duration="60">
|
||||
<div class="title">
|
||||
The "rationing" of living space
|
||||
</div>
|
||||
<div class="video"
|
||||
data-url_0="https://pad.ma/documents/AFL/0,0,6580,3763#embed"
|
||||
data-url_1="https://pad.ma/documents/AFL/1568,1461,3352,2401#embed"
|
||||
></div>
|
||||
<div class="audio"></div> <!-- signal to stop playing audio -->
|
||||
</div>
|
||||
<div class="slide" data-duration="10">
|
||||
<div class="title">
|
||||
The Times of India on 23rd July
|
||||
</div>
|
||||
<div class="video"
|
||||
data-url_0="https://pad.ma/documents/ADZ/0,0,6554,5409#embed",
|
||||
data-url_1="https://pad.ma/documents/ADZ/3836,2088,6401,3440#embed"
|
||||
></div>
|
||||
</div>
|
||||
<div class="slide" data-duration="15">
|
||||
<div class="video" data-url_0="https://pad.ma/BVF/editor/F#embed"
|
||||
></div>
|
||||
|
||||
</div>
|
||||
<div class="slide" data-duration="15">
|
||||
<div class="video" data-url_0="https://indiancine.ma/AKDP/editor/BFR#embed"
|
||||
></div>
|
||||
</div>
|
||||
<!-- YAML parser from: https://github.com/nodeca/js-yaml-->
|
||||
<script src="../lib/js-yaml.min.js"></script>
|
||||
|
||||
<script src="../lib/json-to-html.js"></script>
|
||||
<script src="example.js"></script>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<title>Subscribe to events from pad.ma iframe embed</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe width="640" height="360" src="http://pandora/documents/I/649,254,1289,723#embed" frameborder="0" allowfullscreen id="padembed"></iframe>
|
||||
<iframe width="640" height="360" src="https://pad.ma/BVF/editor/F#embed" frameborder="0" allowfullscreen id="padembed"></iframe>
|
||||
<button id="setUrl">
|
||||
Set Iframe URL
|
||||
</button>
|
||||
|
|
1
lib/js-yaml.min.js
vendored
Normal file
1
lib/js-yaml.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
90
lib/json-to-html.js
Normal file
90
lib/json-to-html.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
|
||||
(function() {
|
||||
|
||||
window.jsonToHTML = getHTML
|
||||
|
||||
function getHTML(slideData) {
|
||||
let video = audio = ''
|
||||
if (!slideData.title) slideData.title = ''
|
||||
title = `
|
||||
<div class="title">
|
||||
${slideData.title}
|
||||
</div>
|
||||
`
|
||||
if (slideData.video) {
|
||||
video = getVideoHtml(slideData.video)
|
||||
}
|
||||
|
||||
if (slideData.audio) {
|
||||
audio = getAudioHtml(slideData.audio)
|
||||
}
|
||||
|
||||
const duration = `data-duration="${slideData.duration ? slideData.duration : 5}"`
|
||||
return `
|
||||
<div class="slide" ${duration}>
|
||||
${title}
|
||||
${video}
|
||||
${audio}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function getVideoHtml(videoData) {
|
||||
let urls = []
|
||||
let volume = ''
|
||||
if (typeof(videoData) === 'string') {
|
||||
urls.push(videoData)
|
||||
} else { // video is an object
|
||||
if (videoData.hasOwnProperty('url')) {
|
||||
urls.push(videoData.url)
|
||||
} else if (videoData.hasOwnProperty('zoom')) {
|
||||
urls = videoData.zoom
|
||||
}
|
||||
if (videoData.volume) {
|
||||
volume = `data-volume="${videoData.volume}"`
|
||||
}
|
||||
}
|
||||
const urlsHtml = urls.map((url, index) => {
|
||||
return `data-url_${index}="${makeEmbed(url)}"`
|
||||
}).join('\n')
|
||||
return `
|
||||
<div class="video" ${urlsHtml} ${volume}> </div>
|
||||
`
|
||||
}
|
||||
|
||||
function getAudioHtml(audioData) {
|
||||
let url = continueStr = volume = ''
|
||||
|
||||
// short circuit to return an empty div if audioData is null or not an object
|
||||
// this is useful for user to specify empty audio track to cause existing
|
||||
// audio to pause
|
||||
if (!audioData || !(typeof(audioData === 'object'))) {
|
||||
return `<div class="audio></div>`
|
||||
}
|
||||
|
||||
if (audioData.url) {
|
||||
url = `data-url="${makeEmbed(audioData.url)}"`
|
||||
}
|
||||
|
||||
if (audioData.volume) {
|
||||
volume = `data-volume="${audioData.volume}"`
|
||||
}
|
||||
|
||||
if (audioData['continue']) {
|
||||
continueStr = `data-continue="true"`
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="audio" ${url} ${volume} ${continueStr}></div>
|
||||
`
|
||||
}
|
||||
|
||||
function makeEmbed(url) {
|
||||
if (!url.endsWith('#embed')) {
|
||||
return `${url}#embed`
|
||||
} else {
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
Loading…
Reference in New Issue
Block a user