Compare commits
No commits in common. "master" and "add-lib" have entirely different histories.
|
@ -1,61 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,302 +0,0 @@
|
||||||
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)
|
|
|
@ -1,9 +0,0 @@
|
||||||
<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>
|
<title>Subscribe to events from pad.ma iframe embed</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<iframe width="640" height="360" src="https://pad.ma/BVF/editor/F#embed" frameborder="0" allowfullscreen id="padembed"></iframe>
|
<iframe width="640" height="360" src="http://10.0.3.230/A/editor/C#embed" frameborder="0" allowfullscreen id="padembed"></iframe>
|
||||||
<button id="setUrl">
|
<button id="setUrl">
|
||||||
Set Iframe URL
|
Set Iframe URL
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -16,7 +16,7 @@ var PandoraEmbed = function(options) {
|
||||||
this.boundFns = {};
|
this.boundFns = {};
|
||||||
var width = options.width || 640;
|
var width = options.width || 640;
|
||||||
var height = options.height || 360;
|
var height = options.height || 360;
|
||||||
var container = options.container || document.body;
|
var container = options.container || document;
|
||||||
if (!options.url) {
|
if (!options.url) {
|
||||||
alert('no embed url specified');
|
alert('no embed url specified');
|
||||||
return;
|
return;
|
||||||
|
@ -40,7 +40,7 @@ var PandoraEmbed = function(options) {
|
||||||
init();
|
init();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('message', this.listener.bind(this), false);
|
window.addEventListener('message', that.listener.bind(this), false);
|
||||||
container.appendChild($iframe);
|
container.appendChild($iframe);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
1
lib/js-yaml.min.js
vendored
1
lib/js-yaml.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,90 +0,0 @@
|
||||||
|
|
||||||
(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