assets/themes/j1/core/js/template.js in j1-template-2023.9.0 vs assets/themes/j1/core/js/template.js in j1-template-2023.9.1
- old
+ new
@@ -4544,13 +4544,18 @@
*/
/* Articulate.js (1.1.0). (C) 2017 Adam Coti.
MIT @license: en.wikipedia.org/wiki/MIT_License
See Github page at: https://github.com/acoti/articulate.js
- See Web site at: http://articulate.purefreedom.com
+ See Web site at: https://purefreedom.com/articulate/
*/
+/* Further reading
+ https://dev.to/jankapunkt/cross-browser-speech-synthesis-the-hard-way-and-the-easy-way-353
+ https://github.com/jankapunkt/easy-speech
+*/
+
(function ($) {
'use strict';
const defaultOptions = __webpack_require__(633);
const ParseContent = __webpack_require__(291);
@@ -4942,35 +4947,41 @@
var chunks = [];
// strip strange elements from text
// unclear why a elements of ' >' is generated in text
// may caused by a HTML tag
+ //
text = text.replace(/^\s+>/gm, '');
text = text.replaceAll(' ..', '.');
// cleanup text
+ //
text = text.replace(/(\r\n|\n|\r)/gm, '');
text = text.replace(/\s+/gm, ' ');
chunks = text.split('.');
// 1st cleanup of chunks
+ //
chunks.forEach((chunk, index) => {
chunks[index] = chunks[index].replace(/^\s+/g, '');
chunks[index] = chunks[index].replaceAll('""', '');
});
// 2nd cleanup of chunks (delete chunks NOT speakable)
+ //
chunks.forEach((chunk, index) => {
if (chunks[index].length > 0) {
chunks[index] = chunks[index] + '. ';
} else {
// remove empty text element from chunks array
+ //
chunks.splice(index, 1);
}
});
// 3rd cleanup of chunks (delete empty chunks)
+ //
chunks.forEach((chunk, index) => {
if (chunks[index].length == 0) {
// remove empty text element from chunks array
chunks.splice(index, 1);
}
@@ -4995,49 +5006,49 @@
offsetTop: offset,
$paragraph: $paragraph
});
});
- // Get headings array
+ // create the headings array
+ //
headingsArray = parseContent.selectHeadings(defaultOptions.contentSelector, defaultOptions.headingSelector);
// parse the headingsArray to add missing offset values
- // for headlines
//
chunkSet.forEach((chunk, index) => {
var text;
var innerText;
if (chunk.offset === undefined) {
// cleanup the spoken text for compare
+ //
text = chunk.text.replaceAll('. ', '');
-
- // jadams:
- // for this type of loop, NOT all headings are found in the array
if (headingsArray !== null) {
// see: https://stackoverflow.com/questions/29285897/difference-between-for-in-and-for-of-statements
// for in loops over enumerable property names of an object
// for of (new in ES6) does use an object-specific iterator
// and loops over the values generated by that.
+ // for (var node in headingsArray) {
for (var node of headingsArray) {
- // for (var node in headingsArray) {
// cleanup the innerText for compare
+ //
innerText = node.innerText.replaceAll('?', '');
- innerText = node.innerText;
+ innerText = node.innerText.replaceAll('!', '');
if (innerText == text) {
var headline = $('#' + node.id);
if (headline.length > 0) {
var offsetTop = headline.offset().top;
chunk.offsetTop = Math.round(offsetTop);
// console.debug('speak2me, text: ' + node.innerText + ', offsetTop: ' + chunk.offsetTop);
} else {
// console.warn('speak2me, text: ' + node.innerText + ', offsetTop not caclulated.');
- }
- }
- }
- }
- }
- });
+ } // END if headline.length
+ } // END if innerText
+ } // END for headingsArray
+ } // END if headingsArray
+ } // END if chunk.offset
+ }); // END forEach chunkSet
+
return chunkSet;
}
// create a slice of text used later to identify the
// containing paragraph
@@ -5047,19 +5058,22 @@
var endSubString = startSubString + slicelenght;
var subText = text.substr(startSubString, endSubString);
var stringArray = subText.split(/(\s+)/);
var words;
- // Remove last two elements are a fraction of subText
+ // remove last two elements are a fraction of subText
+ //
stringArray.pop();
stringArray.pop();
- // built the new string
+ // build the new string
+ //
subText = stringArray.join('');
subText = subText.replaceAll('.', '');
- // at least two words required
+ // at least wordsMin words required
+ //
words = wordCount(subText);
if (words < wordsMin) {
return undefined;
console.warn('no search possible on this fraction of subText');
} else {
@@ -5074,50 +5088,39 @@
// indicate active converter in the quicklinks bar
//
$('.mdib-speaker').addClass('mdib-spin');
- // manage scrolling and highlightning for the active spoken text
+ // listener to ENABLE highlightning and scrolling
+ // on active spoken elements
//
speaker.addEventListener('start', event => {
- // store current scroll position
+ // scroll on ALL valid offsetTop for headings and paragraphs
//
if (speaker.offsetTop !== undefined) {
- speaker.currentScrollPosition = speaker.offsetTop;
- }
-
- // adjust scrolling position offsetTop for 'post series'
- //
- if ($('.bmd-layout-header').length) {
- speaker.offsetTop = $('.bmd-layout-header')[0].offsetTop + (scrollBlockOffset / 2 + 3) + speaker.offsetTop;
- speaker.previousScrollPosition = speaker.offsetTop;
- }
-
- // add highlightning
- //
- if (speaker.$paragraph !== undefined) {
- speaker.$paragraph.addClass('speak-highlighted');
- }
-
- // scroll to paragraph currently spoken if a valid
- // offsetTop is available
- //
- if (speaker.offsetTop !== undefined) {
+ // skip scrolling if offsetTop position is LOWER than expected
+ //
if (speaker.offsetTop >= speaker.previousScrollPosition) {
window.scrollTo({
top: speaker.offsetTop - scrollBlockOffset,
behavior: scrollBehavior
});
}
}
+
+ // manage highlightning on currently spoken paragraph
+ //
+ if (speaker.$paragraph !== undefined) {
+ speaker.$paragraph.addClass('speak-highlighted');
+ }
});
- // listener to manage highlightning for spoken text elements
- // and set next chunk to speak
+ // listener to STOP highlightning for already spoken
+ // text elements and set next chunk to speak
//
speaker.addEventListener('end', function (event) {
- // workaround WRONG offsetTop positions (LOWER as expected)
+ // workaround to detect offsetTop positions LOWER than expected
//
if (speaker.offsetTop !== undefined) {
if (speaker.offsetTop >= speaker.previousScrollPosition) {
speaker.previousScrollPosition = speaker.offsetTop;
}
@@ -5130,11 +5133,11 @@
}
chunkSpoken = false;
chunkCounter++;
});
- // loop to prepare chunks to speak or sto the voice output
+ // loop to prepare ALL chunks to speak or STOP the voice output
//
var speechMonitor = setInterval(function () {
// check if all chunks (text) are spoken
//
if (chunkCounter == chunkCounterMax || userStoppedSpeaking) {
@@ -5668,9 +5671,21 @@
// rather than resolving into their actual character.
//
var txt = document.createElement('textarea');
txt.innerHTML = final;
final = txt.value;
+
+ // Replace single word in line
+ //
+ final = final.replace(/^\s*(\b\w+\b)\s*$/gm, "$1. ");
+
+ // Replace month year in line
+ //
+ final = final.replace(/^\s*(\b\w+\b\s*[0-9]{4})$/gm, "$1. ");
+
+ // Replace multiple whitespaces
+ //
+ final = final.replace(/\s+/g, ' ');
// split the final text in to chunks (sentences).
//
const textChunks = splitTextIntoChunks(final);
chunkCounterMax = textChunks.length;
\ No newline at end of file