Compare commits

..

25 Commits

Author SHA1 Message Date
sanj
32a9720e6e Merge branch 'fetch-textb' of sanj/padmaEmbeds into master 2018-04-02 07:32:50 +00:00
8794207e1d allow loading of yaml with url param textb=<textb_url> 2018-03-27 17:56:21 +05:30
0a4825e14b update slides from yaml 2018-03-18 16:47:21 +05:30
27e8d44db0 fix isReady() for documents 2018-03-17 18:21:39 +05:30
961678fbca Merge branch 'master' of code.with.camp:sanj/padmaEmbeds 2018-03-17 15:47:31 +05:30
78c13a285b wait for slide to load before starting, fixes #3 2018-03-17 15:47:13 +05:30
67cce94580 add border 0 to audio iframe 2018-03-17 15:20:24 +05:30
j
3284c5ca81 use border: 0 for iframe 2018-03-17 09:50:16 +00:00
j
2cc261b604 Merge branch 'refactor-class' of sanj/padmaEmbeds into master 2018-03-17 09:45:19 +00:00
be68a2429b remove unused slideData variable 2018-03-17 15:14:24 +05:30
16d7ffa19a refactor to a Slide class 2018-03-17 14:42:14 +05:30
sanj
ba2e8e4d75 Merge branch 'volume-control' of sanj/padmaEmbeds into master 2018-03-16 14:32:35 +00:00
18cdcee3c1 allow setting of volume via data-volume 2018-03-16 20:00:57 +05:30
j
79c00627a3 Merge branch 'add-audio' of sanj/padmaEmbeds into master 2018-03-16 14:10:54 +00:00
65378ee564 allows continuing and stopping of audio, with slightly strange semantics 2018-03-16 19:20:00 +05:30
8d4f7be7a5 basic audio working without continue 2018-03-16 18:25:14 +05:30
j
034832f71e Merge branch 'many-documents' of sanj/padmaEmbeds into master 2018-03-15 10:24:54 +00:00
3fd440a849 cleanup timeouts for zooms 2018-03-14 23:21:14 +05:30
9ee095b6db work for an arbitrary number of document zooms 2018-03-14 22:56:26 +05:30
9cb4300004 merge commit 2018-03-09 22:12:10 +05:30
bc374ef513 video auutoplay, reset video 2018-03-09 22:11:27 +05:30
j
440a78b691 enable timeout 2018-03-09 21:47:37 +05:30
j
54896ff1c3 some form on presentation demo 2018-03-09 21:46:02 +05:30
d7864c6d36 minor cleanups 2018-03-06 19:40:03 +05:30
sanj
c43759e8b2 Merge branch 'add-lib' of sanj/padmaEmbeds into master 2018-03-06 12:38:59 +00:00
7 changed files with 468 additions and 5 deletions

61
example/example.css Normal file
View 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
View 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
View 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>

View File

@ -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>

View File

@ -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

File diff suppressed because one or more lines are too long

90
lib/json-to-html.js Normal file
View 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
}
}
})();