From a9c50e0feaae038b0e9c99d28033a2290d354361 Mon Sep 17 00:00:00 2001
From: Mark Boas
Date: Thu, 23 Sep 2021 15:15:44 +0200
Subject: [PATCH] added wm ha lite version
---
wp-hyperaudio/hyperaudio-admin.php | 18 +
wp-hyperaudio/hyperaudio.php | 10 +-
wp-hyperaudio/js/hyperaudio-lite.js | 679 +++++++++++++++++-----------
3 files changed, 432 insertions(+), 275 deletions(-)
diff --git a/wp-hyperaudio/hyperaudio-admin.php b/wp-hyperaudio/hyperaudio-admin.php
index 2c4a535..99128ae 100644
--- a/wp-hyperaudio/hyperaudio-admin.php
+++ b/wp-hyperaudio/hyperaudio-admin.php
@@ -183,8 +183,26 @@ function hyperaudio_options_page()
media-height | 640px | set the height of the audio or video |
font-family | Arial, Helvetica, sans-serif; | set the font family of the transcript |
id | mytranscript | sets the id of the trancript for sharing purposes |
+ webmonetization | true | enables Web Monetization (false by default) |
+ For example:
+ [hyperaudio src="https://example.com/video/video.mp4" width="90%" transcript-height="600px" webmonetization=true]
+ <article>
+ <section>
+ <p>
+ <span data-m="4470" data-d="0" class="speaker">Doc: </span>
+ <span data-m="4470" data-d="270">We </span>
+ <span data-m="4740" data-d="240">have </span>
+ <span data-m="5010" data-d="300">two </span>
+ <span data-m="5310" data-d="600">selves </span>
+ ...
+ </p>
+ </section>
+ </article>
+ [/hyperaudio]
+
+
And for those with the caption generating version ...
diff --git a/wp-hyperaudio/hyperaudio.php b/wp-hyperaudio/hyperaudio.php
index 87d2f6f..fac47dd 100644
--- a/wp-hyperaudio/hyperaudio.php
+++ b/wp-hyperaudio/hyperaudio.php
@@ -31,6 +31,7 @@ function hyperaudio_shortcode_handler($atts, $transcript, $tag)
$captionsOn = true;
$language = "en";
$trackLabel = "English";
+ $webmonetization = false;
if (isset($atts['src'])) $src = esc_html__($atts['src']);
if (isset($atts['width'])) $width = $atts['width'];
@@ -46,6 +47,8 @@ function hyperaudio_shortcode_handler($atts, $transcript, $tag)
if (isset($atts['language'])) $language = $atts['language'];
if (isset($atts['track-label'])) $trackLabel = $atts['track-label'];
+ if (isset($atts['webmonetization'])) $webmonetization = $atts['webmonetization'];
+
$transcript = preg_replace( "/\r|\n/", "", $transcript);
@@ -174,8 +177,11 @@ function hyperaudio_shortcode_handler($atts, $transcript, $tag)
selector: "article"
}).init();
- var ht1 = hyperaudiolite();
- ht1.init("'.$transcriptid.'", "hyperplayer'.$id.'", false);';
+ var minimizedMode = false;
+ var autoScroll = true;
+ var doubleClick = false;
+
+ new HyperaudioLite("'.$transcriptid.'", "hyperplayer'.$id.'", minimizedMode, autoScroll, doubleClick, '.$webmonetization.');';
if ($captionsOn == true) {
$o .= 'var cap1 = caption();
diff --git a/wp-hyperaudio/js/hyperaudio-lite.js b/wp-hyperaudio/js/hyperaudio-lite.js
index 73c5d1d..fdbd2d4 100644
--- a/wp-hyperaudio/js/hyperaudio-lite.js
+++ b/wp-hyperaudio/js/hyperaudio-lite.js
@@ -1,196 +1,227 @@
/*! (C) The Hyperaudio Project. MIT @license: en.wikipedia.org/wiki/MIT_License. */
-/*! Version 1.1.0 */
+/*! Version 2.0.4 */
'use strict';
-var hyperaudiolite = (function () {
-
- var hal = {},
- transcript,
- player,
- paraIndex,
- wordIndex,
- textShot,
- paras,
- start,
- end,
- timer,
- minimizedMode,
- wordArr = [],
- playerType,
- currentTime,
- windowHash,
- hashArray,
- hashVar;
-
-
- function init(mediaElementId, m) {
-
- windowHash = window.location.hash;
-
- hashVar = windowHash.substring(1,windowHash.indexOf("="));
-
-
- if (hashVar === transcript.id) {
- hashArray = windowHash.substr(transcript.id.length+2).split(',');
+class HyperaudioLite {
+ constructor(transcriptId, mediaElementId, minimizedMode, autoscroll, doubleClick, webMonetization) {
+ this.transcript = document.getElementById(transcriptId);
+ this.init(mediaElementId, minimizedMode, autoscroll, doubleClick, webMonetization);
+ }
+
+ init = (mediaElementId, m, a, d, w) => {
+ const windowHash = window.location.hash;
+ const hashVar = windowHash.substring(1, windowHash.indexOf('='));
+
+ if (hashVar === this.transcript.id) {
+ this.hashArray = windowHash.substr(this.transcript.id.length + 2).split(',');
} else {
- hashArray = [];
+ this.hashArray = [];
}
- transcript.addEventListener('mouseup', function() {
+ document.addEventListener(
+ 'selectionchange',
+ () => {
+ const mediaFragment = this.getSelectionMediaFragment();
+
+ if (mediaFragment !== '') {
+ document.location.hash = mediaFragment;
+ }
+ },
+ false,
+ );
- var mediaFragment = getSelectionMediaFragment();
+ this.minimizedMode = m;
+ this.textShot = '';
+ this.wordIndex = 0;
- if ( mediaFragment !== "") {
- document.location.hash = mediaFragment;
- }
- }, false);
+ this.autoscroll = a;
+ this.scrollerContainer = this.transcript;
+ this.scrollerOffset = 0;
+ this.scrollerDuration = 800;
+ this.scrollerDelay = 0;
- minimizedMode = m;
- textShot = "";
- wordIndex = 0;
+ this.doubleClick = d;
+ this.webMonetization = w;
//Create the array of timed elements (wordArr)
- var words = transcript.querySelectorAll('[data-m]');
+ const words = this.transcript.querySelectorAll('[data-m]');
- for (var i = 0; i < words.length; ++i) {
- var m = parseInt(words[i].getAttribute('data-m'));
- var p = words[i].parentNode;
- while (p !== document) {
- if (p.tagName.toLowerCase() === 'p' || p.tagName.toLowerCase() === 'figure' || p.tagName.toLowerCase() === 'ul') {
- break;
- }
- p = p.parentNode;
- }
- wordArr[i] = { 'n': words[i], 'm': m, 'p': p }
- }
+ this.wordArr = this.createWordArray(words);
- for (var i = 0; i < wordArr.length; ++i) {
- wordArr[i].n.classList.add("unread");
- }
-
- paras = transcript.getElementsByTagName('p');
+ this.paras = this.transcript.getElementsByTagName('p');
- player = document.getElementById(mediaElementId);
+ this.player = document.getElementById(mediaElementId);
- if (player.tagName == "VIDEO" || player.tagName == "AUDIO") { //native HTML media elements
- playerType = "native";
- } else { //assume it is a SoundCloud or YouTube iframe
- playerType = player.getAttribute("data-player-type");
+ if (this.player.tagName == 'VIDEO' || this.player.tagName == 'AUDIO') {
+ //native HTML media elements
+ this.playerType = 'native';
+ } else {
+ //assume it is a SoundCloud or YouTube iframe
+ this.playerType = this.player.getAttribute('data-player-type');
}
- if (playerType == "native") {
- player.addEventListener('pause', clearTimer, false);
- player.addEventListener('play', checkPlayHead, false);
- } else if (playerType == "soundcloud"){ // SoundCloud
- player = SC.Widget(mediaElementId);
- player.bind(SC.Widget.Events.PAUSE, clearTimer);
- player.bind(SC.Widget.Events.PLAY, checkPlayHead);
- } else { // assume YouTube
- var tag = document.createElement('script');
+ if (this.playerType === 'native') {
+ this.player.addEventListener('pause', this.clearTimer, false);
+ this.player.addEventListener('play', this.checkPlayHead, false);
+ } else if (this.playerType === 'soundcloud') {
+ // SoundCloud
+ this.player = SC.Widget(mediaElementId);
+ this.player.bind(SC.Widget.Events.PAUSE, this.clearTimer);
+ this.player.bind(SC.Widget.Events.PLAY, this.checkPlayHead);
+ } else {
+ // assume YouTube
+ const tag = document.createElement('script');
tag.id = 'iframe-demo';
tag.src = 'https://www.youtube.com/iframe_api';
- var firstScriptTag = document.getElementsByTagName('script')[0];
+ const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
- window.onYouTubeIframeAPIReady = function() {
- player = new YT.Player(mediaElementId, {
+ window.onYouTubeIframeAPIReady = function () {
+ this.player = new YT.Player(mediaElementId, {
events: {
- 'onStateChange': onPlayerStateChange
- }
+ onStateChange: onPlayerStateChange,
+ },
});
- }
+ };
+
+ onPlayerStateChange = event => {
+ if (event.data === 1) {
+ // playing
+ this.checkPlayHead();
+ } else if (event.data === 2) {
+ // paused
+ this.clearTimer();
+ }
+ };
+ }
- function onPlayerStateChange(event) {
- if (event.data == 1) { // playing
- checkPlayHead();
- } else if (event.data == 2) { // paused
- clearTimer();
- }
- }
+ this.paraIndex = 0;
+
+ words[0].classList.add('active');
+ this.paras[0].classList.add('active');
+
+ let playHeadEvent = 'click';
+
+ if (this.doubleClick === true) {
+ playHeadEvent = 'dblclick';
}
-
- paraIndex = 0;
- words[0].classList.add("active");
- paras[0].classList.add("active");
- transcript.addEventListener('click', setPlayHead, false);
- transcript.addEventListener('click', checkPlayHead, false);
- start = hashArray[0];
+ this.transcript.addEventListener(playHeadEvent, this.setPlayHead, false);
+ this.transcript.addEventListener(playHeadEvent, this.checkPlayHead, false);
- if (!isNaN(parseFloat(start))) {
+ const start = this.hashArray[0];
- if (playerType == "native") {
- player.currentTime = start;
+ if (!isNaN(parseFloat(start))) {
+ if (this.playerType === 'native') {
+ this.player.currentTime = start;
//autoplay
- var promise = player.play();
+ const promise = this.player.play();
if (promise !== undefined) {
- promise.catch(error => {
- console.log("Auto-play prevented");
- }).then(() => {
+ promise
+ .catch(error => {
+ console.log('Auto-play prevented');
+ })
+ .then(() => {
// Auto-play started
- });
+ });
}
- } else if (playerType == "soundcloud"){ // SoundCloud
- player.seekTo(start * 1000);
- } else { // Assume YouTube
- window.onYouTubeIframeAPIReady = function() {
- player = new YT.Player(mediaElementId, {
- playerVars: { 'autoplay': 1 },
+ } else if (this.playerType === 'soundcloud') {
+ // SoundCloud
+ this.player.seekTo(start * 1000);
+ } else {
+ // Assume YouTube
+
+ window.onYouTubeIframeAPIReady = function () {
+ this.player = new YT.Player(mediaElementId, {
+ playerVars: { autoplay: 1 },
events: {
- 'onReady': function() {
- player.seekTo(start, true);
- player.playVideo();
- }
- }
+ onReady: function () {
+ this.player.seekTo(start, true);
+ this.player.playVideo();
+ },
+ },
});
- }
+ };
}
}
- end = hashArray[1];
+ this.end = this.hashArray[1];
//TODO convert to binary search for below for quicker startup
- if (start && end) {
- for (var i = 1; i < words.length; i++) {
- var wordStart = parseInt(words[i].getAttribute("data-m"))/1000;
- if ( wordStart > start && end > wordStart ) {
- words[i].classList.add("share-match");
+ if (start && this.end) {
+ for (let i = 1; i < words.length; i++) {
+ const wordStart = parseInt(words[i].getAttribute('data-m')) / 1000;
+ if (wordStart > start && this.end > wordStart) {
+ words[i].classList.add('share-match');
}
}
}
- }
- function getSelectionMediaFragment() {
+ if (this.autoscroll === true) {
+ this.scroller = window.Velocity || window.jQuery.Velocity;
+ }
+ };
- var fragment = "";
- var selection = null;
+ createWordArray = words => {
+ let wordArr = [];
+
+ words.forEach((word, i) => {
+ const m = parseInt(word.getAttribute('data-m'));
+ let p = word.parentNode;
+ while (p !== document) {
+ if (
+ p.tagName.toLowerCase() === 'p' ||
+ p.tagName.toLowerCase() === 'figure' ||
+ p.tagName.toLowerCase() === 'ul'
+ ) {
+ break;
+ }
+ p = p.parentNode;
+ }
+ wordArr[i] = { n: words[i], m: m, p: p };
+ wordArr[i].n.classList.add('unread');
+ });
+
+ return wordArr;
+ };
+
+ getSelectionMediaFragment = () => {
+ let fragment = '';
+ let selection = null;
if (window.getSelection) {
- selection = window.getSelection();
+ selection = window.getSelection();
} else if (document.selection) {
- selection = document.selection.createRange();
+ selection = document.selection.createRange();
}
if (selection.toString() !== '') {
+ let fNode = selection.focusNode.parentNode;
+ let aNode = selection.anchorNode.parentNode;
+
+ if (aNode.tagName === "P") {
+ aNode = selection.anchorNode.nextElementSibling;
+ }
- var fNode = selection.focusNode.parentNode;
- var aNode = selection.anchorNode.parentNode;
+ if (fNode.tagName === "P") {
+ fNode = selection.focusNode.nextElementSibling;
+ }
- if (aNode.getAttribute('data-m') == null || aNode.className == "speaker") {
- aNode = aNode.nextElementSibling;
+ if (aNode.getAttribute('data-m') === null || aNode.className === 'speaker') {
+ aNode = aNode.nextElementSibling;
}
- if (fNode.getAttribute('data-m') == null || fNode.className == "speaker") {
- fNode = fNode.previousElementSibling;
+ if (fNode.getAttribute('data-m') === null || fNode.className === 'speaker') {
+ fNode = fNode.previousElementSibling;
}
- var aNodeTime = parseInt(aNode.getAttribute('data-m'), 10);
- var aNodeDuration = parseInt(aNode.getAttribute('data-d'), 10);
- var fNodeTime;
- var fNodeDuration;
+ let aNodeTime = parseInt(aNode.getAttribute('data-m'), 10);
+ let aNodeDuration = parseInt(aNode.getAttribute('data-d'), 10);
+ let fNodeTime;
+ let fNodeDuration;
if (fNode != null && fNode.getAttribute('data-m') != null) {
fNodeTime = parseInt(fNode.getAttribute('data-m'), 10);
@@ -204,200 +235,302 @@ var hyperaudiolite = (function () {
fNodeTime = Math.round(fNodeTime / 100) / 10;
fNodeDuration = Math.round(fNodeDuration / 100) / 10;
- var nodeStart = aNodeTime;
- var nodeDuration = Math.round((fNodeTime + fNodeDuration - aNodeTime) * 10) / 10;
+ let nodeStart = aNodeTime;
+ let nodeDuration = Math.round((fNodeTime + fNodeDuration - aNodeTime) * 10) / 10;
if (aNodeTime >= fNodeTime) {
nodeStart = fNodeTime;
nodeDuration = Math.round((aNodeTime + aNodeDuration - fNodeTime) * 10) / 10;
}
- if (nodeDuration == 0 || nodeDuration == null || isNaN(nodeDuration)) {
+ if (nodeDuration === 0 || nodeDuration === null || isNaN(nodeDuration)) {
nodeDuration = 10; // arbitary for now
}
- fragment = transcript.id+ "=" + nodeStart + "," + (Math.round((nodeStart + nodeDuration) * 10) / 10);
+ fragment = this.transcript.id + '=' + nodeStart + ',' + Math.round((nodeStart + nodeDuration) * 10) / 10;
}
- return (fragment);
- }
+ return fragment;
+ };
- function setPlayHead(e) {
-
- var target = (e.target) ? e.target : e.srcElement;
- target.setAttribute("class", "active");
- var timeSecs = parseInt(target.getAttribute("data-m")) / 1000;
-
- if(!isNaN(parseFloat(timeSecs))) {
- end = null;
- if (playerType == "native"){
- player.currentTime = timeSecs;
- player.play();
- } else if (playerType == "soundcloud"){
- player.seekTo(timeSecs * 1000);
- player.play();
- } else { //assume YouTube
- player.seekTo(timeSecs, true);
- player.playVideo();
- }
- }
- }
+ setPlayHead = e => {
+ const target = e.target ? e.target : e.srcElement;
- function clearTimer() {
- if (timer) clearTimeout(timer);
- }
+ // clear elements with class='active'
- function checkPlayHead() {
+ let activeElements = Array.from(this.transcript.getElementsByClassName('active'));
- clearTimer();
+ activeElements.forEach(e => {
+ e.classList.remove('active');
+ });
- if (playerType == "native"){
- currentTime = player.currentTime;
- } else if (playerType == "soundcloud"){
- player.getPosition(function(ms) {
- currentTime = ms / 1000;
+ target.setAttribute('class', 'active');
+
+ const timeSecs = parseInt(target.getAttribute('data-m')) / 1000;
+
+ if (!isNaN(parseFloat(timeSecs))) {
+ this.end = null;
+
+ if (this.playerType === 'native') {
+ this.player.currentTime = timeSecs;
+ this.player.play();
+ } else if (this.playerType === 'soundcloud') {
+ this.player.seekTo(timeSecs * 1000);
+ this.player.play();
+ } else {
+ //assume YouTube
+
+ this.player.seekTo(timeSecs, true);
+ this.player.playVideo();
+ }
+ }
+ };
+
+ clearTimer = () => {
+ if (this.timer) clearTimeout(this.timer);
+ };
+
+ checkPlayHead = () => {
+ this.clearTimer();
+
+ if (this.playerType === 'native') {
+ this.currentTime = this.player.currentTime;
+ this.checkStatus();
+ } else if (this.playerType === 'soundcloud') {
+ this.player.getPosition(ms => {
+ this.currentTime = ms / 1000;
+ this.checkStatus();
});
- } else { // assume YouTube
- currentTime = player.getCurrentTime();
+ } else {
+ // assume YouTube
+ this.currentTime = this.player.getCurrentTime();
+ this.checkStatus();
}
+ }
+ checkStatus = () => {
//check for end time of shared piece
- if (end && (end < currentTime)) {
- player.pause();
- end = null;
+ if (this.end && this.end < this.currentTime) {
+ this.player.pause();
+ this.end = null;
} else {
- var newPara = false;
- var interval; // used to establish next checkPlayHead
- var index = 0;
- var words = wordArr.length - 1;
-
- // Binary search https://en.wikipedia.org/wiki/Binary_search_algorithm
- while (index <= words) {
- var guessIndex = index + ((words - index) >> 1); // >> 1 has the effect of halving and rounding down
- var difference = wordArr[guessIndex].m / 1000 - currentTime; // wordArr[guessIndex].m represents start time of word
-
- if (difference < 0) { // comes before the element
- index = guessIndex + 1;
- }
- else if (difference > 0) { // comes after the element
- words = guessIndex - 1;
- }
- else { // equals the element
- index = guessIndex;
- break;
- }
- }
+ let newPara = false;
+ let interval = 0; // used to establish next checkPlayHead
- for (var i = 0; i < index; ++i) {
- wordArr[i].n.classList.add("read");
- wordArr[i].n.classList.remove("unread");
- }
+ let indices = this.updateTranscriptVisualState();
- for (var i = index; i < wordArr.length; ++i) {
- wordArr[i].n.classList.add("unread");
- wordArr[i].n.classList.remove("read");
- }
+ let scrollNode = null;
- for (var i = 0; i < index; ++i) {
- wordArr[i].n.classList.remove("active");
- }
+ let index = indices.currentWordIndex;
+ let currentParaIndex = indices.currentParaIndex;
- paras = transcript.getElementsByTagName('p');
+ if (index > 0) {
+ scrollNode = this.wordArr[index - 1].n.parentNode;
- //remove active class from all paras
+ if (scrollNode !== null && scrollNode.tagName != 'P') {
+ // it's not inside a para so just use the element
+ scrollNode = this.wordArr[index - 1].n;
+ }
- for (var a = 0; a < paras.length; a++) {
- if (paras[a].classList.contains("active")) {
- paras[a].classList.remove("active");
+ if (currentParaIndex != this.paraIndex) {
+ if (typeof this.scroller !== 'undefined' && this.autoscroll === true) {
+ if (scrollNode !== null) {
+ if (typeof this.scrollerContainer !== 'undefined' && this.scrollerContainer !== null) {
+ this.scroller(scrollNode, 'scroll', {
+ container: this.scrollerContainer,
+ duration: this.scrollerDuration,
+ delay: this.scrollerDelay,
+ offset: this.scrollerOffset,
+ });
+ } else {
+ this.scroller(scrollNode, 'scroll', {
+ duration: this.scrollerDuration,
+ delay: this.scrollerDelay,
+ offset: this.scrollerOffset,
+ });
+ }
+ } else {
+ // the wordlst needs refreshing
+ let words = this.transcript.querySelectorAll('[data-m]');
+ this.wordArr = this.createWordArray(words);
+ this.paras = this.transcript.getElementsByTagName('p');
+ }
+ }
+
+ newPara = true;
+
+ this.paraIndex = currentParaIndex;
}
}
- // set current word and para to active
+ //minimizedMode is still experimental - it changes document.title upon every new word
- if (index > 0) {
- wordArr[index - 1].n.classList.add("active");
- wordArr[index - 1].n.parentNode.classList.add("active");
- }
+ if (this.minimizedMode) {
+ const elements = transcript.querySelectorAll('[data-m]');
+ let currentWord = '';
+ let lastWordIndex = this.wordIndex;
- if (wordArr[index]) {
- interval = parseInt(wordArr[index].n.getAttribute('data-m') - currentTime * 1000);
- } else {
- interval = 0;
- }
+ for (let i = 0; i < elements.length; i++) {
+ if ((' ' + elements[i].className + ' ').indexOf(' active ') > -1) {
+ currentWord = elements[i].innerHTML;
+ this.wordIndex = i;
+ }
+ }
- // Establish current paragraph index
+ let textShot = '';
- var currentParaIndex;
+ if (this.wordIndex != lastWordIndex) {
+ textShot = textShot + currentWord;
+ }
- for (var a = 0; a < paras.length; a++) {
- if (paras[a].classList.contains("active")) {
- currentParaIndex = a;
- break;
+ if (textShot.length > 16 || newPara === true) {
+ document.title = textShot;
+ textShot = '';
+ newPara = false;
}
}
- var scrollNode = null;
+ if (this.wordArr[index]) {
+ interval = parseInt(this.wordArr[index].n.getAttribute('data-m') - this.currentTime * 1000);
+ }
- if (index > 0) {
- scrollNode = wordArr[index-1].n.parentNode;
+ this.timer = setTimeout(() => {
+ this.checkPlayHead();
+ }, interval + 1); // +1 to avoid rounding issues (better to be over than under)
+ }
- if (scrollNode.tagName != "P") { // it's not inside a para so just use the element
- scrollNode = wordArr[index-1].n;
- }
+ if (this.webMonetization === true) {
+ //check for payment pointer
- if (currentParaIndex != paraIndex) {
- Velocity(scrollNode, "scroll", {
- container: transcript,
- duration: 800,
- delay: 0
- });
+ let activeElements = this.transcript.getElementsByClassName('active');
- newPara = true;
+ let paymentPointer = this.checkPaymentPointer(activeElements[activeElements.length - 1]);
- paraIndex = currentParaIndex;
+ if (paymentPointer !== null) {
+ let metaElements = document.getElementsByTagName('meta');
+ let wmMeta = document.querySelector("meta[name='monetization']");
+ if (wmMeta === null) {
+ wmMeta = document.createElement('meta');
+ wmMeta.name = 'monetization';
+ wmMeta.content = paymentPointer;
+ document.getElementsByTagName('head')[0].appendChild(wmMeta);
+ } else {
+ wmMeta.name = 'monetization';
+ wmMeta.content = paymentPointer;
}
}
+ }
+ };
- //minimizedMode is still experimental - it changes document.title upon every new word
+ checkPaymentPointer = element => {
+ let paymentPointer = element.getAttribute('data-wm');
- if (minimizedMode) {
+ if (paymentPointer !== null) {
+ return paymentPointer;
+ } else {
+ let parent = element.parentElement;
- var elements = transcript.querySelectorAll('[data-m]');
- var currentWord = "";
- var lastWordIndex = wordIndex;
+ if (parent === null) {
+ return null;
+ } else {
+ return this.checkPaymentPointer(parent);
+ }
+ }
+ };
+
+ updateTranscriptVisualState = () => {
+ let index = 0;
+ let words = this.wordArr.length - 1;
+
+ // Binary search https://en.wikipedia.org/wiki/Binary_search_algorithm
+ while (index <= words) {
+ const guessIndex = index + ((words - index) >> 1); // >> 1 has the effect of halving and rounding down
+ const difference = this.wordArr[guessIndex].m / 1000 - this.currentTime; // wordArr[guessIndex].m represents start time of word
+
+ if (difference < 0) {
+ // comes before the element
+ index = guessIndex + 1;
+ } else if (difference > 0) {
+ // comes after the element
+ words = guessIndex - 1;
+ } else {
+ // equals the element
+ index = guessIndex;
+ break;
+ }
+ }
- for (var i = 0; i < elements.length; i++) {
- if((' ' + elements[i].className + ' ').indexOf(' active ') > -1) {
- currentWord = elements[i].innerHTML;
- wordIndex = i;
- }
- }
+ this.wordArr.forEach((word, i) => {
+ let classList = word.n.classList;
+ if (i < index) {
+ classList.add('read');
+ classList.remove('unread');
+ classList.remove('active');
+ } else {
+ classList.add('unread');
+ classList.remove('read');
+ }
+ });
- if (wordIndex != lastWordIndex) {
- textShot = textShot + currentWord;
- }
+ this.paras = this.transcript.getElementsByTagName('p');
- if (textShot.length > 16 || newPara == true) {
- document.title = textShot;
- textShot = "";
- newPara = false;
- }
+ //remove active class from all paras
+
+ Array.from(this.paras).forEach(para => {
+ if (para.classList.contains('active')) {
+ para.classList.remove('active');
}
+ });
- timer = setTimeout(function() {
- checkPlayHead();
- }, interval+1); // +1 to avoid rounding issues (better to be over than under)
- }
+ // set current word and para to active
- }
+ if (index > 0) {
+ this.wordArr[index - 1].n.classList.add('active');
+ if (this.wordArr[index - 1].n.parentNode !== null) {
+ this.wordArr[index - 1].n.parentNode.classList.add('active');
+ }
+ }
- hal.init = function(transcriptId, mediaElementId, minimizedMode) {
- transcript = document.getElementById(transcriptId);
- init(mediaElementId, minimizedMode);
- //set minimizedMode is an experimental feature
- }
+ // Establish current paragraph index
- return hal;
+ let currentParaIndex;
-});
\ No newline at end of file
+ Array.from(this.paras).every((para, i) => {
+ if (para.classList.contains('active')) {
+ currentParaIndex = i;
+ return false;
+ }
+ return true;
+ });
+
+ let indices = {
+ currentWordIndex: index,
+ currentParaIndex: currentParaIndex,
+ };
+
+ return indices;
+ };
+
+ setScrollParameters = (duration, delay, offset, container) => {
+ this.scrollerContainer = container;
+ this.scrollerDuration = duration;
+ this.scrollerDelay = delay;
+ this.scrollerOffset = offset;
+ };
+
+ toggleAutoScroll = () => {
+ this.autoscroll = !this.autoscroll;
+ };
+
+ setAutoScroll = state => {
+ this.autoscroll = state;
+ };
+}
+
+// required for testing
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = { HyperaudioLite };
+}
+
+//export default HyperaudioLite;