Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
32a9720e6e | ||
8794207e1d | |||
0a4825e14b | |||
27e8d44db0 | |||
961678fbca | |||
78c13a285b | |||
67cce94580 | |||
|
3284c5ca81 | ||
|
2cc261b604 | ||
be68a2429b | |||
16d7ffa19a | |||
|
ba2e8e4d75 | ||
18cdcee3c1 | |||
|
79c00627a3 | ||
65378ee564 | |||
8d4f7be7a5 | |||
|
034832f71e | ||
3fd440a849 | |||
9ee095b6db | |||
9cb4300004 | |||
bc374ef513 | |||
440a78b691 | |||
54896ff1c3 | |||
d7864c6d36 | |||
|
c43759e8b2 |
61
example/example.css
Normal file
61
example/example.css
Normal file
|
@ -0,0 +1,61 @@
|
|||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
font-size: 18px;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.base {
|
||||
z-index: 1;
|
||||
background: black;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.slide {
|
||||
z-index: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-shadow:
|
||||
-2px -2px 0 #000,
|
||||
2px -2px 0 #000,
|
||||
-2px 2px 0 #000,
|
||||
2px 2px 0 #000;
|
||||
|
||||
}
|
||||
.title {
|
||||
font-size: 48px;
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
margin-top: 30%;
|
||||
text-align: center;
|
||||
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
height: 56px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.video {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.video iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.audio iframe {
|
||||
height: 0;
|
||||
border: 0;
|
||||
}
|
302
example/example.js
Normal file
302
example/example.js
Normal file
|
@ -0,0 +1,302 @@
|
|||
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 = [],
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
function init() {
|
||||
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].sendToBack()
|
||||
slides[idx].bringToFront()
|
||||
current = idx
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
timeout = null
|
||||
}
|
||||
timeout = setTimeout(next, Math.round(slides[current].duration) * 1000)
|
||||
|
||||
slides[old].stop()
|
||||
slides[current].start()
|
||||
}
|
||||
|
||||
function next() {
|
||||
var idx = (current + 1) % slides.length
|
||||
console.log(current, idx)
|
||||
go(idx)
|
||||
}
|
||||
|
||||
function previous() {
|
||||
var idx = (current - 1) % slides.length
|
||||
if (idx < 0) {
|
||||
idx += slides.length
|
||||
}
|
||||
go(idx)
|
||||
}
|
||||
|
||||
|
||||
function load_urls(dataset) {
|
||||
var urls = [], idx = 0
|
||||
while (dataset['url_' + idx]) {
|
||||
urls.push(dataset['url_' + idx])
|
||||
idx += 1
|
||||
}
|
||||
return urls
|
||||
}
|
||||
|
||||
|
||||
window.addEventListener('blur', function(){
|
||||
setTimeout(function(){
|
||||
// using the 'setTimout' to let the event pass the run loop
|
||||
if (document.activeElement instanceof HTMLIFrameElement) {
|
||||
window.focus();
|
||||
}
|
||||
},0);
|
||||
}, false);
|
||||
|
||||
|
||||
function reset_active_audio() {
|
||||
if (activeAudio) {
|
||||
activeAudio.resetAudio()
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(event) {
|
||||
if (event.key == 'ArrowRight') {
|
||||
next()
|
||||
event.preventDefault()
|
||||
} else if (event.key == 'ArrowLeft') {
|
||||
previous()
|
||||
event.preventDefault()
|
||||
}
|
||||
}, false)
|
9
example/index.html
Normal file
9
example/index.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<meta charset="utf-8">
|
||||
<script src="../lib/PandoraEmbed.js"></script>
|
||||
<link type="text/css" href="example.css" rel="stylesheet" />
|
||||
|
||||
<!-- 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://10.0.3.230/A/editor/C#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>
|
||||
|
@ -50,7 +50,7 @@ document.getElementById('setUrl').addEventListener('click', function(e) {
|
|||
$iframe.addEventListener('load', () => {
|
||||
let timeout;
|
||||
|
||||
/*
|
||||
/*
|
||||
We can't be sure that the JS inside the iframe will be fully loaded and ready to receive messages. Therefore, we need to keep resending the init event until we are sure we've received the acknowledgement reply from the embed. This function calls itself every 250ms until `isInit` on the iframe object is set to true. The listener above sets this to true once it received the init acknowledgement.
|
||||
*/
|
||||
function init() {
|
||||
|
@ -72,4 +72,4 @@ $iframe.addEventListener('load', () => {
|
|||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -16,7 +16,7 @@ var PandoraEmbed = function(options) {
|
|||
this.boundFns = {};
|
||||
var width = options.width || 640;
|
||||
var height = options.height || 360;
|
||||
var container = options.container || document;
|
||||
var container = options.container || document.body;
|
||||
if (!options.url) {
|
||||
alert('no embed url specified');
|
||||
return;
|
||||
|
@ -40,7 +40,7 @@ var PandoraEmbed = function(options) {
|
|||
init();
|
||||
});
|
||||
|
||||
window.addEventListener('message', that.listener.bind(this), false);
|
||||
window.addEventListener('message', this.listener.bind(this), false);
|
||||
container.appendChild($iframe);
|
||||
return this;
|
||||
};
|
||||
|
|
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