function ttsHiliteModule(window) { var window = window, document = window.document; function getXPath(element) { var xpath = ''; for (; element && element.nodeType == 1; element = element.parentNode) { /* jQuery code: var id = $(element.parentNode).children(element.tagName).index(element) + 1; */ /* calculate the index of occurance of the "element" w.r.t the parentNode */ var checkPrevSiblingsOfElement = element.previousSibling; var id = 1; while (checkPrevSiblingsOfElement != null) { // It becomes null when it runs out of previous nodes if (checkPrevSiblingsOfElement.nodeType === 1) { // Execute only if previousNode is an ELEMENT_NODE (especially omit text nodes) if (checkPrevSiblingsOfElement.tagName.toLowerCase() === element.tagName.toLowerCase()) { id++; } } checkPrevSiblingsOfElement = checkPrevSiblingsOfElement.previousSibling; } id > 1 ? (id = '[' + id + ']') : (id = ''); xpath = '/' + element.tagName.toLowerCase() + id + xpath; } return xpath; } function applyHighlight(doc, serializedStr, className) { var rangeParams = base64.decode(serializedStr).split(","); var startOffset = parseInt(rangeParams[2]); var endOffset = parseInt(rangeParams[3]); var startContainerRelation = rangeParams[0].split("::")[1]; var endContainerRelation = rangeParams[1].split("::")[1]; var startContainer, endContainer; if (startContainerRelation !== "null") { startContainer = rangeParams[0].split("::")[1] === "prevSib" ? Hilite.getElementFromXPath(doc, rangeParams[0].split("::")[0]).nextSibling : Hilite.getElementFromXPath(doc, rangeParams[0].split("::")[0]).childNodes[0]; if (startContainer.nodeType === 1 && startContainer.className.split(" ")[0] === "highlight") { startContainer = startContainer.childNodes[0]; } //step 1. check if offsets are available in the textNode if (startContainer.length <= startOffset) { var shiftedStart = Hilite.shiftContainerAndOffset(startContainer, startOffset); startContainer = shiftedStart.shiftedContainer; startOffset = shiftedStart.shiftedOffset; } } else { startContainer = Hilite.getElementFromXPath(doc, rangeParams[0].split("::")[0]); } if (endContainerRelation !== "null") { endContainer = rangeParams[1].split("::")[1] === "prevSib" ? Hilite.getElementFromXPath(doc, rangeParams[1].split("::")[0]).nextSibling : Hilite.getElementFromXPath(doc, rangeParams[1].split("::")[0]).childNodes[0]; if (endContainer.nodeType === 1 && endContainer.className.split(" ")[0] === "highlight") { endContainer = endContainer.childNodes[0]; } //step 1. check if offsets are available in the textNode if (endContainer.length < endOffset) { var shiftedEnd = Hilite.shiftContainerAndOffset(endContainer, endOffset); endContainer = shiftedEnd.shiftedContainer; endOffset = shiftedEnd.shiftedOffset; } } else { endContainer = Hilite.getElementFromXPath(doc, rangeParams[1].split("::")[0]); } var rangeObj = doc.createRange(); rangeObj.setStart(startContainer, startOffset); rangeObj.setEnd(endContainer, endOffset); getNodesInRange(rangeObj, doc, className); } function getNodesInRange(rangeObj, doc, className) { var xpaths = []; /* NR - array of XPATH addresses */ //this.removeClasses = []; /* Merged classes, needs to be removed */ var range = rangeObj, nodes = [], /* Collection of text nodes to be wrapped */ mergeClasses = [], /* NR - Elements are objects {"className": classHighlight[1], "element": node, "position": position} */ newStartContainer = false, newEndContainer = false, mergeClassesSeen = []; /* To maintain uniqueness of merged classes */ /* When documnet body gets selected */ if (navigator.userAgent.toLowerCase().indexOf("ipad") > -1 || navigator.userAgent.toLowerCase().indexOf("iphone") > -1) { if (range.startContainer === doc.body || range.endContainer === doc.body) { return "invalid: selected"; } } /* start container or end container is null logic */ if (range.startContainer === null && range.endContainer === null) { return "invalid: (SC,EC)=NULL"; } else if (range.startContainer === null) { range.startContainer = range.endContainer; } else if (range.endContainer === null) { range.endContainer = range.startContainer; } /* fix for firefox - when the selected region has endContainer before the startContainer (or) has "\n\t" text nodes*/ var nodesBeforeStartContainer = []; for (var node = doc.getElementsByTagName('body')[0], i = 0; node; node = Hilite.getNextNode(node)) { if (node === range.startContainer) { break; } else { nodesBeforeStartContainer[i] = node; i++; } } if (Hilite.arrayContainsObject(nodesBeforeStartContainer, range.endContainer)) { range.setEnd(range.startContainer, range.endOffset); } if (range.endContainer.nodeType === 3) { var tempEndNode = range.endContainer.nodeValue.replace(/[\t\n\r]/g, ""); if (tempEndNode.length === 0) { //range.setEnd(range.startContainer, range.endOffset); range.setEnd(range.startContainer, range.startContainer.length); } } /* end of fix */ // populate nodes[] & mergeClasses[] for (var node = range.startContainer, position = 0; node; node = Hilite.getNextNode(node)) { if (node.nodeType === 3) { /* text nodes which does not belong to SVG */ /* filter out text nodes with escape sequences */ var nodeLength = node.nodeValue.replace(/[ \t\n\r]/g, ""); if (nodeLength.length === 0) { continue; } // push textNodes not wrapped in nodes with "highlight" class to nodes[] /* if the text is child node of of , type: svg */ if (node.parentNode.nodeName === "text") nodes.push({ "type": "svgText", "element": node, "position": position++ }); else nodes.push({ "type": "default", "element": node, "position": position++ }); } // if the nodes in selection contain the "highlight" class then its a merge operation if (node == range.endContainer) break; } //Here the selection is a subset of the existing selection if (nodes.length === 0) { if (mergeClasses.length !== 0) { if (Hilite.merge === true) { //return the selection if it is the duplicate of an existing, before returning return savedSelection; } else { return; } } else { return "invalid: no text"; } } //1. If range.startContainer == range.endContainer, then slection made within the same Element // also in case of merge highlight, this condition would fail //if(range.startContainer == range.endContainer){or if (nodes.length === 1) { var startOffset, endOffset, xpathValues, xpath; if (range.startContainer.previousSibling) { //check for "highlight" class //var classHighlight = range.startContainer.previousSibling.getAttribute('class').split(" "); var classHighlight = (range.startContainer.previousSibling.getAttribute('class') !== null) ? range.startContainer.previousSibling.getAttribute('class').split(" ") : [null]; if (classHighlight[0] === 'highlight') { xpathValues = range.startContainer.previousSibling.getAttribute("data-offset"); xpathValues = base64.decode(xpathValues).split("|"); startOffset = range.startOffset + parseInt(xpathValues[1]); endOffset = range.endOffset + parseInt(xpathValues[1]); relation = xpathValues[2]; xpath = xpathValues[3]; } else { startOffset = range.startOffset; endOffset = range.endOffset; relation = "prevSib"; xpath = getXPath(range.startContainer.previousSibling); } } else { startOffset = range.startOffset; endOffset = range.endOffset; relation = "parentNode"; xpath = getXPath(range.startContainer.parentNode); } var dataOffset = startOffset + "|" + endOffset + "|" + relation + "|" + xpath; if (window.getSelection) { if (nodes[0].type === "svgText") { var newNode = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); newNode.setAttribute('class', 'highlight ' + className); } else { var newNode = doc.createElement(Hilite.hiliteTag); newNode.className = newNode.className + "highlight " + className; } // Split the text var text1 = nodes[0].element.nodeValue.substr(0, range.startOffset); var text2 = nodes[0].element.nodeValue.substr(range.startOffset, range.endOffset - range.startOffset); var text3 = nodes[0].element.nodeValue.substr(range.endOffset); newNode.appendChild(doc.createTextNode(text2)); newNode.setAttribute('data-offset', base64.encode(dataOffset)); //range.deleteContents(); //range.insertNode(newNode); // Replace with existing text nodes[0].element.parentNode.replaceChild(newNode, nodes[0].element); // Put text1 before newNode if (text2.length > 0) { newNode.parentNode.insertBefore(doc.createTextNode(text1), newNode); } // Put text3 before newNode if (text3.length > 0) { if (newNode.nextSibling) { newNode.parentNode.insertBefore(doc.createTextNode(text3), newNode.nextSibling); } else { newNode.parentNode.appendChild(doc.createTextNode(text3)); } } } else if (window.document.selection) { var rangeIE = doc.selection.createRange(); rangeIE.pasteHTML("" + rangeIE.text + "\n for (var i = 0; i < nodes.length; i++) { if (nodes[i].element.nodeType == 3) { //alert(nodes[i].nodeValue.charCodeAt(0)); //To eliminate textNode's with a newline nodeValue //if((nodes[i].nodeValue === "\t\t\n\t\t") || (nodes[i].length <= 0)) //if((nodes[i].nodeValue.indexOf("\t\t\n\t\t") !== -1) || (nodes[i].length <= 0)) //if((nodes[i].nodeValue === "\t\t\n\t\t") || (nodes[i].length <= 0) || (nodes[i].nodeValue === "\n\t\t\n\t\t") || (nodes[i].nodeValue === "\n\t\t")) //var tempNode = nodes[i].nodeValue.replace(/[\t\n\r]/g, ""); //if(tempNode.length === 0){ //continue; //} if (i == 0) { // 1st node - begin from startOffset var startOffset = range.startOffset, xpathValues, relation, xpath; var parent = nodes[i].element.parentNode; // Split the text var text1 = nodes[i].element.nodeValue.substr(0, startOffset); var text2 = nodes[i].element.nodeValue.substr(startOffset); //############################Check for existing SPANS(.highlight) ############################# if (nodes[i].element.previousSibling) { //check for "highlight" in previous node //var classHighlight = nodes[i].element.previousSibling.getAttribute('class').split(" "); var classHighlight = ((typeof nodes[i].element.previousSibling.getAttribute === 'function') && (nodes[i].element.previousSibling.getAttribute('class') !== null)) ? nodes[i].element.previousSibling.getAttribute('class').split(" ") : [null]; if (classHighlight[0] === 'highlight') { xpathValues = nodes[i].element.previousSibling.getAttribute("data-offset"); xpathValues = base64.decode(xpathValues).split("|"); startOffset = range.startOffset + parseInt(xpathValues[1]); endOffset = parseInt(xpathValues[1]) + nodes[i].element.length; relation = xpathValues[2]; xpath = xpathValues[3]; //endOffset = range.endOffset + parseInt(startEndRange[1]); } else { startOffset = range.startOffset; endOffset = nodes[i].element.length; relation = "prevSib"; xpath = getXPath(nodes[i].element.previousSibling); //endOffset = range.endOffset; } } else { startOffset = range.startOffset; endOffset = nodes[i].element.length; relation = "parentNode"; xpath = getXPath(nodes[i].element.parentNode); //endOffset = range.endOffset; } //################################################################################### //wrap the slected text //var wrapper = doc.createElement('span'); if (nodes[i].type === "svgText") { var wrapper = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); wrapper.setAttribute('class', 'highlight ' + className); } else { var wrapper = doc.createElement(Hilite.hiliteTag); wrapper.className = wrapper.className + "highlight " + className; } // set element as child of wrapper wrapper.appendChild(doc.createTextNode(text2)); // Replace with existing text parent.replaceChild(wrapper, nodes[i].element); // Put text1 before wrapper parent.insertBefore(doc.createTextNode(text1), wrapper); //set class, id and data-offset //wrapper.className = wrapper.className + "highlight " + className; var dataOffset = startOffset + "|" + endOffset + "|" + relation + "|" + xpath; //EOE - End Of Element wrapper.setAttribute('data-offset', base64.encode(dataOffset)); xpaths[nodes[i].position] = base64.encode(dataOffset); } else if (i == (nodes.length - 1)) { //last node - stop at endOffset var endOffset = range.endOffset, relation, xpath; var parent = nodes[i].element.parentNode; // Split the text var text1 = nodes[i].element.nodeValue.substr(0, endOffset); var text2 = nodes[i].element.nodeValue.substr(endOffset); //############################ Get offsets|relation|xpath for the last element #############################c //############################Check for existing SPANS(.highlight) ############################# if (nodes[i].element.previousSibling) { //check for "highlight" in previous node var classHighlight = ((typeof nodes[i].element.previousSibling.getAttribute === 'function') && (nodes[i].element.previousSibling.getAttribute('class') !== null)) ? nodes[i].element.previousSibling.getAttribute('class').split(" ") : [null]; if (classHighlight[0] === 'highlight') { xpathValues = nodes[i].element.previousSibling.getAttribute("data-offset"); xpathValues = base64.decode(xpathValues).split("|"); startOffset = parseInt(xpathValues[1]); endOffset = range.endOffset + parseInt(xpathValues[1]); relation = xpathValues[2]; xpath = xpathValues[3]; //endOffset = range.endOffset + parseInt(startEndRange[1]); } else { startOffset = 0; endOffset = range.endOffset; relation = "prevSib"; xpath = getXPath(nodes[i].element.previousSibling); //endOffset = range.endOffset; } } else { startOffset = 0; endOffset = range.endOffset; relation = "parentNode"; xpath = getXPath(nodes[i].element.parentNode); //endOffset = range.endOffset; } //################################################################################### //################################################################################### //wrap the slected text //var wrapper = doc.createElement('span'); if (nodes[i].type === "svgText") { var wrapper = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); wrapper.setAttribute('class', 'highlight ' + className); } else { var wrapper = doc.createElement(Hilite.hiliteTag); wrapper.className = wrapper.className + "highlight " + className; } // set text1 as child of wrapper wrapper.appendChild(doc.createTextNode(text1)); // Replace with existing text parent.replaceChild(wrapper, nodes[i].element); // append text2 to the wrapper parent.insertBefore(doc.createTextNode(text2), wrapper.nextSibling); //set class and data-offset attr //wrapper.className = wrapper.className + "highlight " + className; var dataOffset = startOffset + "|" + endOffset + "|" + relation + "|" + xpath; wrapper.setAttribute('data-offset', base64.encode(dataOffset)); xpaths[nodes[i].position] = base64.encode(dataOffset); } else { // else apply full highlight var parent = nodes[i].element.parentNode, relation, xpath; //############################ Get relation|xpath for the inbetween elements ############################# //############################Check for existing SPANS(.highlight) ############################# if (nodes[i].element.previousSibling) { //check for "highlight" in previous node var classHighlight = ((typeof nodes[i].element.previousSibling.getAttribute === 'function') && (nodes[i].element.previousSibling.getAttribute('class') !== null)) ? nodes[i].element.previousSibling.getAttribute('class').split(" ") : [null]; if (classHighlight[0] === 'highlight') { xpathValues = nodes[i].element.previousSibling.getAttribute("data-offset"); xpathValues = base64.decode(xpathValues).split("|"); startOffset = parseInt(xpathValues[1]); endOffset = startOffset + nodes[i].element.length; relation = xpathValues[2]; xpath = xpathValues[3]; //endOffset = range.endOffset + parseInt(startEndRange[1]); } else { startOffset = 0; endOffset = nodes[i].element.length; relation = "prevSib"; xpath = getXPath(nodes[i].element.previousSibling); //endOffset = range.endOffset; } } else { startOffset = 0; endOffset = nodes[i].element.length; relation = "parentNode"; xpath = getXPath(nodes[i].element.parentNode); //endOffset = range.endOffset; } //################################################################################### //wrap the slected text //var wrapper = doc.createElement('span'); if (nodes[i].type === "svgText") { var wrapper = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); wrapper.setAttribute('class', 'highlight ' + className); } else { var wrapper = doc.createElement(Hilite.hiliteTag); wrapper.className = wrapper.className + "highlight " + className; } // set the wrapper as child (instead of the element) parent.replaceChild(wrapper, nodes[i].element); // set element as child of wrapper wrapper.appendChild(nodes[i].element); //set class and data-attr //wrapper.className = wrapper.className + "highlight " + className; var dataOffset = startOffset + "|" + endOffset + "|" + relation + "|" + xpath; wrapper.setAttribute('data-offset', base64.encode(dataOffset)); //EOE - End Of Element xpaths[nodes[i].position] = base64.encode(dataOffset); } } } } /* ADD ID TO THE FIST TEXT NODE */ //alert('asasasas: '+ className); doc.getElementsByClassName(className)[0].setAttribute('id', className.split(" ")[0]); //Hilite.log(nodes); //var serializedString = ""; //for(var i=0; i < xpaths.length; i++) //serializedString += xpaths[i] + ","; //return serializedString; //return savedSelection; } function serializeHilite(hiliteData) { var sRelation, eRelation, sPath, ePath; if (hiliteData.startNode.previousSibling) { sRelation = "prevSib"; sPath = getXPath(hiliteData.startNode.previousSibling) } else { sRelation = "parentNode"; sPath = getXPath(hiliteData.startNode.parentNode) } if (hiliteData.endNode.previousSibling) { eRelation = "prevSib"; ePath = getXPath(hiliteData.endNode.previousSibling) } else { eRelation = "parentNode"; ePath = getXPath(hiliteData.endNode.parentNode) } return base64.encode(sPath + '::' + sRelation + ',' + ePath + '::' + eRelation + ',' + hiliteData.startOffset + ',' + hiliteData.endOffset); } return { applyHighlight: applyHighlight, serializeHilite: serializeHilite, getXPath: getXPath } } function ttsParserModule(window) { var window = window, document = window.document, //exceptions array previously had '.' in it, was removed to handle a CONTENT ISSUE where a '.' came alone and as a result two lines were //getting higlighted together. Since the chances of a dot coming alone with spaces before and after it are slim, this change shouldnt create regression. exceptions = ['Mr.', 'Dr.', 'Mrs.', 'Ms.', 'Pres.', 'Gov.', 'U.S.', 'v.', 'al.'], inlineTags = ['SPAN', 'SMALL', 'EM', 'A', 'SUB', 'SUP', 'U', 'B', 'I', 'STRONG','FONT'], blockTags = ['P', 'DIV', 'ARTICLE', 'SECTION', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'ASIDE', 'NAV', 'HEADER', 'FOOTER', 'IMG', 'BR','LI'], currNode = null, endNode = null, startNode = null, serializedCurrNode = null, sentence = '', carryOver = false, /* carry over sentence from previous tect node */ carryOverIndex = 0, textNodeLengthParsed = 0, words = []; /* public methods */ function reset() { currNode = serializedCurrNode = null; carryOverIndex = textNodeLengthParsed = 0; sentence = ''; carryOver = false; words = []; } function breakOnTag() { /*currNode.parantNode = inline tag OR currNode.nextSibling = inline tag Also, there is no text node available until the next Block node (e.g.,

)*/ if (inlineTags.indexOf(currNode.parentNode.tagName.toUpperCase()) > -1 || (currNode.nextSibling && inlineTags.indexOf(currNode.nextSibling.tagName.toUpperCase()) > -1)) { for (var node = currNode; node; node = getNextNode(node)) { if (node.nodeType === 3 && node.nodeValue.trim().length > 0 && node !== currNode) return false; else if (node.nodeType === 1 && blockTags.indexOf(node.tagName.toUpperCase()) > -1) break; } } return true; } function getSentence() { var ready = false, textNode, startOffset, tagBreak = false, terminate = false, trim = false; sentence = ''; startNode = null; if (currNode !== null) { currNode = deserializeTextNode(serializedCurrNode); } // set end node to null after speakText if (endNode !== null) { if (!carryOver) { currNode = getTextNode(); } ready = true; sentence += currNode.textContent; startNode = currNode; startOffset = 0; textNodeLengthParsed = currNode.length + 1; if (currNode === endNode) terminate = true; if (carryOver) { carryOver = false; } } while (!ready) { sentence = sentence.trim(); /* remove any trailing spaces */ if (!carryOver) { textNode = getTextNode(); if (typeof textNode === "undefined") return undefined; /* end of audio */ tagBreak = breakOnTag(); if (startNode === null) { startNode = textNode; startOffset = 0 } if (!/[\.!\?]/g.test(textNode.textContent)) { sentence += textNode.textContent; if (tagBreak) { textNodeLengthParsed = currNode.length + 1; break; } else continue; } else { words = textNode.nodeValue.trim().split(" "); carryOverIndex = 0; textNodeLengthParsed = 0; } } else { carryOver = false; startNode = currNode; startOffset = textNodeLengthParsed; } if (words && words instanceof Array) { for (var i = carryOverIndex, l = words.length, w = null; i < l; i++) { textNodeLengthParsed += words[i].length + 1; w = words[i].trim(); sentence += w + ' '; if (/[\.!\?]/g.test(w.charAt(w.length - 1)) && exceptions.indexOf(w) === -1) { ready = true; if (l - i > 1) { carryOver = true; carryOverIndex = ++i } break; } } if (tagBreak && !ready) { /* if sentences have exceptions words */ ready = true; textNodeLengthParsed = currNode.length + 1; break; } } } serializedCurrNode = serializeTextNode(currNode); return { sentence: sentence, hiliteData: ttsHilite.serializeHilite({ startNode: startNode, endNode: currNode, startOffset: startOffset, endOffset: textNodeLengthParsed - 1 }), terminate: terminate } } function getTextNode() { if (currNode === null) { // first time currNode = document.body; } while (1) { currNode = getNextNode(currNode); if (currNode === undefined) { currNode = document.body; return undefined; } if (currNode.nodeType === 3 && currNode.nodeValue.trim().length > 0) { return currNode; } } } function serializeTextNode(textNode) { var path, relation; if (textNode.previousSibling) { relation = "prevSib"; path = ttsHilite.getXPath(textNode.previousSibling) } else { relation = "parentNode"; path = ttsHilite.getXPath(textNode.parentNode) } //console.log(path + '::' + relation) return path + '::' + relation; } function deserializeTextNode(serializedString) { var path = serializedString.split('::')[0], relation = serializedString.split('::')[1], element; element = Hilite.getElementFromXPath(document, path); if (relation === "parentNode") { element = element.childNodes[0] } else { element = element.nextSibling } //console.log(element) return element; } function getNextNode(node) { if (node.firstChild) return node.firstChild; while (node) { if (node.nextSibling) return node.nextSibling; node = node.parentNode; } } function setCurrNode(node) { currNode = node; currNode = getTextNode(); serializedCurrNode = serializeTextNode(currNode); carryOver = true; carryOverIndex = 0; textNodeLengthParsed = 0; words = currNode.nodeValue.trim().split(" "); } function setEndNode(node) { endNode = node; } function getEndNode(node) { return endNode; } function getCurrNode() { return currNode } function getStartNode() { return startNode } return { getNextNode: getNextNode, getSentence: getSentence, reset: reset, setCurrNode: setCurrNode, setEndNode: setEndNode, getEndNode: getEndNode, getCurrNode: getCurrNode, getStartNode: getStartNode } } function tts(window, readerWindow, serviceUrl, scrollDirection, serviceInfo, readText) {// Vivek : aaded new parameter serviceInfo to accomodate new api which required jwt token, and readText to read a particular text /* private variables */ var window = window, document = window.document, ttsEndPt = serviceUrl.trim().length !== 0 ? serviceUrl : 'http://tts-api.com/tts.mp3?q={text}', currSent = null, nextSent = null, currHilite = null, nextHilte = null, currAudio = document.createElement('audio'), nextAudio = document.createElement('audio'), speakClass = 'tts-speak', currTerminate = false, nextTerminate = false, speak = false, click = false, active = false, uA = navigator.userAgent.toLowerCase(), mobile = (uA.indexOf("android") > -1 || uA.indexOf("ipad") > -1 || uA.indexOf("iphone") > -1) ? true : false, stopEvent = readerWindow.document.createEvent("HTMLEvents"), scrollDirection = scrollDirection === undefined ? 'vertical' : scrollDirection; playAfterLoad = false, ttsVoice = 'Emma'; ttsSourceLanguage = "en"; ttsPlaybackSpeed = 1; playOnlyAudioElementFlag = false; voiceListObject = { en: ["Emma", "Joanna", "Ivy", "Joey", "Kendra", "Justin", "Mathieu", "Kimberly", "Salli", "Brian", "Amy", "Russell", "Nicole", "Aditi", "Raveena", "Geraint"], es: ["Lupe", "Penelope", "Miguel", "Conchita", "Lucia", "Enrique", "Mia"], fr: ["Celine", "Léa", "Mathieu"], da: ["Naja", "Mads"], nl: ["Lotte", "Ruben"], de: ["Marlene", "Vicki", "Hans"], hi: ["Aditi"], it: ["Carla", "Bianca", "Giorgio"], ja: ["Mizuki", "Takumi"], ko: ["Seoyeon"], pl: ["Ewa", "Maja", "Jacek", "Jan"], pt: ["Camila", "Vitoria", "Ricardo", "Ines", "Cristiano"], sv: ["Astrid"], arb: ["Zeina"] }; retryCount = 0; // $("#toggleTtsIcon .ttsModal").remove(); stopEvent.initEvent("TtsStopped", true, true); /* private functions */ function init() { ttsParser = ttsHilite = undefined; playAfterLoad = false; currAudio = document.createElement('audio'); nextAudio = document.createElement('audio'); /* Initialize ttsParser and ttsHilite modules */ if (typeof ttsHilite === "undefined" || typeof ttsParser === "undefined") { ttsHilite = ttsHiliteModule(window); ttsParser = ttsParserModule(window); } if (!speak && !click) scrollHandlerHorizontal(); /* locating text not required for speak tts */ //currAudio = document.createElement('audio'); var currPassage = ttsParser.getSentence(); if(!currPassage) { $("#ttsPause").css('display', 'none'); $("#ttsPlay").css('display', 'inline-block'); return; } currSent = currPassage.sentence; currHilite = currPassage.hiliteData; currTerminate = currPassage.terminate; if (!mobile) { sendRequest(currAudio, currSent, false); //nextAudio = document.createElement('audio'); if (!currTerminate) { var nextPassage = ttsParser.getSentence(); if (typeof nextPassage === "undefined") { /* end of audio */ currTerminate = true; } else { nextSent = nextPassage.sentence; nextHilite = nextPassage.hiliteData; nextTerminate = nextPassage.terminate; sendRequest(nextAudio, nextSent, false); } } nextAudio.addEventListener('canplay', canPlayHandler); // nextAudio.addEventListener('timeupdate', timeUpdateHandler); nextAudio.addEventListener('ended', endedHandler); } /* Event Listensers */ // if (!mobile) document.addEventListener('mousedown', clickHandler); currAudio.addEventListener('canplay', canPlayHandler); // currAudio.addEventListener('timeupdate', timeUpdateHandler); currAudio.addEventListener('ended', endedHandler); currAudio.addEventListener('error', errorHandler);//Added to handel server errors -- Minati //document.addEventListener('touchend', clickHandler); parent.document.getElementById(dlayer).addEventListener('scroll', scrollHandler); } function updateState(prevFlag) { // prevFlag :: differentiation b/w next and prev if (prevFlag) { alert('prev coming soon...'); } else { if (mobile) { var currPassage = ttsParser.getSentence(); if (typeof currPassage === "undefined") { /* end of audio */ stop(true); return; } else { currSent = currPassage.sentence; currHilite = currPassage.hiliteData; currTerminate = currPassage.terminate; } } else { if (!playAfterLoad) { var tempAudio = currAudio; currAudio = nextAudio; nextAudio = tempAudio; playAfterLoad = true; currSent = nextSent; currHilite = nextHilite; currTerminate = nextTerminate; if (!currTerminate) { var nextPassage = ttsParser.getSentence(); if (typeof nextPassage === "undefined") { /* end of audio */ currTerminate = true; } else { nextSent = nextPassage.sentence; nextHilite = nextPassage.hiliteData; nextTerminate = nextPassage.terminate; sendRequest(nextAudio, nextSent, false); } } if (active) play(); } else { retryCount--; if (retryCount > 0) setTimeout(function () { updateState() }, 50); return; } } } } function applyHilite(hiliteData) { ttsHilite.applyHighlight(document, hiliteData, 'tts-highlight'); var hilight = document.getElementsByClassName('tts-highlight'); /* for(var i = 0; i < hilight.length; i++){ hilight[i].style.backgroundColor = '#F5EBDB'; hilight[i].style.color = 'rgb(82, 2, 2)'; hilight[i].style.boxShadow = '#D6C19F 0px 1px 2px 1px'; hilight[i].style.zIndex = '10'; } */ /* scroll the TTS document */ if (scrollDirection === 'horizontal') { var leftOffset = document.querySelector('.tts-highlight').getBoundingClientRect().left; var columnWidth; if (document.getElementsByTagName('html')[0].style.webkitColumnWidth !== undefined) { columnWidth = parseInt(document.getElementsByTagName('html')[0].style.webkitColumnWidth); } else if (document.getElementsByTagName('html')[0].style.MozColumnWidth !== undefined) { columnWidth = parseInt(document.getElementsByTagName('html')[0].style.MozColumnWidth); } var columnGap; if (document.getElementsByTagName('html')[0].style.webkitColumnGap !== undefined) { columnGap = parseInt(document.getElementsByTagName('html')[0].style.webkitColumnGap); } else if (document.getElementsByTagName('html')[0].style.MozColumnGap !== undefined) { columnGap = parseInt(document.getElementsByTagName('html')[0].style.MozColumnGap); } var columnCount = parseInt(document.getElementsByTagName('html')[0].style.webkitColumnCount); columnCount = isNaN(columnCount) ? 1 : columnCount; if(columnCount === 2 && isNaN(columnWidth)) { columnWidth = parseInt(document.getElementsByTagName('html')[0].style.width); columnWidth = (columnWidth / 2) - (columnGap / 2); } var screenWidth = (columnWidth + columnGap) * columnCount; var shiftScreens = Math.floor(leftOffset / screenWidth); //window.scrollTo(window.scrollX + (shiftScreens * screenWidth), 0); for (var i = 0, count = Math.abs(shiftScreens); i < count; i++) { if (shiftScreens > 0) $(window.parent.document.querySelector('#next')).trigger('click'); else $(window.parent.document.querySelector('#prev')).trigger('click'); } } else if (scrollDirection === 'vertical') { var topOffset = document.querySelector('.tts-highlight').parentNode.getBoundingClientRect().top, scrollTop = $(window).scrollTop() + topOffset - 100; $("#scrolled-content-frame").animate({ scrollTop: scrollTop }); // window.scrollTo(0, scrollTop); } } function removeHilite() { var highlighNodes = document.getElementsByClassName('tts-highlight'), parent, childNodes, text; while (highlighNodes.length > 0) { parent = highlighNodes[0].parentNode, childNodes = parent.childNodes, text = highlighNodes[0].removeChild(highlighNodes[0].firstChild); parent.insertBefore(text, highlighNodes[0]); parent.removeChild(highlighNodes[0]); /* To find out the childNode index of the highlighted text after normalization - Eg:

*/ for (var i = 0, l = childNodes.length; i < l; i++) { if (childNodes[i] === text) break } while (i >= 0 && childNodes[i].previousSibling !== null && childNodes[i].previousSibling.nodeType === 3) { i-- } /* end of logic */ (navigator.userAgent.indexOf('rv:11')) === -1 ? parent.normalize() : Hilite.normalize(parent); /* parent.normalize(); */ } } function reset() { removeHilite(); ttsParser.reset(); } function setVoice(voice) { ttsVoice = voice; currAudio.pause(); if($("#ttsPlay").is(":visible")) { playOnlyAudioElementFlag = true; } sendRequest(currAudio, currSent, true); // currAudio.play(); // updateState(); } function setScrollDirection(direction) { scrollDirection = direction; } function setPlaybackSpeed(speed) { currAudio.playbackRate = speed; nextAudio.playbackRate = speed; ttsPlaybackSpeed = speed; } function getSourceLanguage(text ,cb) { if(!text) { cb({ SourceLanguageCode: "en" }); } else { var inputText = text; var sourceLanguageCode = 'auto'; var targetLanguageCode = "en"; var params = { Text: inputText, SourceLanguageCode: sourceLanguageCode, TargetLanguageCode: targetLanguageCode }; AWS.config.region = 'us-east-1'; // Region var that = this; //AWS.config.credentials = new AWS.Credentials("access key", "secret key"); AWS.config.credentials = new AWS.Credentials("AKIAWTAXOEBHTZXLS6M6", "hB16iVgHZ/J+UY6zi1E8TClhb2yKbE2g2AUpIEJ2"); this.translate = new AWS.Translate({ region: AWS.config.region }); this.polly = new AWS.Polly(); this.translate.translateText(params, function (err, data) { if (err) { console.log(err, err.stack); //alert("Error calling " + err.message); //return; cb(null); } if (data) { cb(data); } }); } } function sendRequest(audioElement, text, currAudioPlayFlag) { var readingText = readText ? readText : text; if (!readingText) { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var audioUrl = JSON.parse(this.responseText).data.url playAfterLoad = false; audioElement.setAttribute('src', audioUrl); audioElement.playbackRate = ttsPlaybackSpeed; if (currAudioPlayFlag) { if ($("#ttsPause").is(":visible")) { audioElement.play(); } else { audioElement.pause(); } sendRequest(nextAudio, nextSent, false); playAfterLoad = true; } } }; var audioUrl = ttsEndPt.replace('{text}', text).replace('{voice}', ttsVoice); xhttp.open("GET", audioUrl, true); xhttp.setRequestHeader("Content-type", "application/json"); xhttp.setRequestHeader("X-Jwt-Token", serviceInfo.jwtToken); xhttp.send(); } else { getSourceLanguage(readingText, function (res) { if (!res) { res = { SourceLanguageCode: ttsSourceLanguage } } if (ttsSourceLanguage != res.SourceLanguageCode) { ttsVoice = voiceListObject[res.SourceLanguageCode][0]; ttsSourceLanguage = res.SourceLanguageCode; if (document.getElementById("voiceListContainer")) { /* document.getElementById("voiceListContainer").innerHTML = voiceListObject[res.SourceLanguageCode].map((data) => { return `

${data}

` }).join(""); */ document.getElementById("voiceListContainer").innerHTML = voiceListObject[res.SourceLanguageCode].map(function (el) { return "

${data}

" }).join(""); // var vector = labelsPrint.map(function (el) { return el.id; }); let childTags = document.getElementById("voiceListContainer").querySelectorAll("p"); for (let index = 0; index < childTags.length; index++) { childTags[index].classList.remove("active"); if (childTags[index].innerHTML === ttsVoice) { childTags[index].classList.add("active"); } } } } text = readText ? encodeURIComponent(readText) : encodeURIComponent(text); // Fixing -> TTS skip the highlighted text after semicolon //console.log('Sending requesting'); console.log(ttsEndPt.replace('{text}', text)); // Vivek: changes to accomodate new api var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var audioUrl = JSON.parse(this.responseText).data.url playAfterLoad = false; audioElement.setAttribute('src', audioUrl); audioElement.playbackRate = ttsPlaybackSpeed; if (currAudioPlayFlag) { if ($("#ttsPause").is(":visible")) { audioElement.play(); } else { audioElement.pause(); } sendRequest(nextAudio, nextSent, false); playAfterLoad = true; } } }; var audioUrl = ttsEndPt.replace('{text}', text).replace('{voice}', ttsVoice); xhttp.open("GET", audioUrl, true); xhttp.setRequestHeader("Content-type", "application/json"); xhttp.setRequestHeader("X-Jwt-Token", serviceInfo.jwtToken); xhttp.send(); //audioElement.setAttribute('src', ttsEndPt.replace('{text}', text)); if (mobile) { showMsg('buffering'); } else { if (currAudio.readyState !== 4) showMsg('buffering'); } }); } } /* public functions */ function play() { if(playOnlyAudioElementFlag) { currAudio.play(); playOnlyAudioElementFlag = false; return; } if (!active) { init() } /* if first play initialize*/ if (currSent.length === 0) { stop(true); return } if (currAudio.paused && currAudio.currentTime > 0 && currAudio.currentTime < currAudio.duration) { currAudio.play(); return } applyHilite(currHilite); $iframe=$("#epubContentIframe").contents().find(".tts-highlight")[0]; if($iframe) { let windowHeight = $("#epubContentIframe").contents().find("html").height(); } if (mobile) sendRequest(currAudio, currSent, false); currAudio.play(); active = true; //sendRequest(nextAudio, nextSent); } function pause() { currAudio.pause(); hideMsg('buffering', false); //console.log('Paused'); } function stop(endOfPage) { if(!active) { return; } if(currAudio.duration) { currAudio.pause(); } if (ttsParser.getEndNode !== null) { removeHilite(); clearSpeak(); } reset(); //init(); active = false; // document.getElementById("ttsPause").style.display = 'none'; // document.getElementById("ttsPlay").style.display = 'inline-block'; $("#ttsPause").css('display', 'none'); $("#ttsPlay").css('display', 'inline-block'); if (endOfPage){ setTimeout(function() { if(currAudio.paused) { $("#toggleTtsIcon .ttsModal").hide(); } }, 5000); readerWindow.document.dispatchEvent(stopEvent); } } /* Event handlers */ function endedHandler() { removeHilite(); if (currTerminate) { stop(true); return } /* stop caching audio */ if (currTerminate && speak) { clearSpeak(); stop(); return } /* stop speak audio*/ retryCount = 1000; updateState(); //play(); // if (active) play(); /* active check added to avoid audio replay from the beginning */ } function timeUpdateHandler(e) { if (currAudio.currentTime >= currAudio.duration) { endedHandler(); } } function clickHandler(e) { if (speak) { e.preventDefault(); return; } if (active) { if (getPlayState()) { e.preventDefault(); pause(); reset(); ttsParser.setCurrNode(e.target); click = true; init(); click = false; play(); } else currAudio.pause(); } } function canPlayHandler(e) { hideMsg('buffering', true); } function errorHandler(e) { showMsg('no_network'); } function scrollHandler() { //console.log(active); //if(active) return; var findTextNode = null, prevLikelyElement = null, likelyElement = document.body; /*cuttOff = $(parent.document.getElementById(dlayer)).scrollTop();*/ /* redefine dlayer */ while (prevLikelyElement !== likelyElement) { prevLikelyElement = likelyElement; for (var i = 0, childNodes = likelyElement.childNodes, l = childNodes.length; i < l; i++) { if (childNodes[i].nodeType === 1) { //if(childNodes[i].getBoundingClientRect().top >= 0) break; if (childNodes[i].getBoundingClientRect().top >= cuttOff) break; likelyElement = childNodes[i]; } } } for (var node = likelyElement; node; node = ttsParser.getNextNode(node)) { if (node.nodeType === 3 && node.nodeValue.trim().length > 0 && node.parentNode.getBoundingClientRect().top >= 0) { findTextNode = node; break; } } if (findTextNode === null) return; reset(); ttsParser.setCurrNode(findTextNode.parentNode); //init(); } function scrollHandlerHorizontal() { var findTextNode = null, prevLikelyElement = null, likelyElement = document.body; cuttOff = window.scrollX; for (var node = document.body; node; node = ttsParser.getNextNode(node)) { if (node.nodeType === 1 && node.getBoundingClientRect().left > 0 && node.getBoundingClientRect().left < parseInt(document.getElementsByTagName('html')[0].style.webkitColumnWidth) * 2 + 60) { likelyElement = node; break; } } for (var node = likelyElement; node; node = ttsParser.getNextNode(node)) { if (node.nodeType === 3 && node.nodeValue.trim().length > 0) { findTextNode = node; break; } } if (findTextNode === null) return; reset(); ttsParser.setCurrNode(findTextNode.parentNode); } function showMsg(flag) { switch (flag) { case 'buffering': //console.log('buffering...'); $("#ttsPause").css('display', 'none'); $("#buffer-loader").css('display', 'inline-block'); // $(readerWindow.document.body).append('
Buffering ...
'); break; case 'no_network': //console.log('error!'); $(readerWindow.document.body).append('
TTS server error.
'); errTimer = setTimeout(function () { hideMsg('no_network', true); hideMsg('buffering', true); }, 5000); break; } } function hideMsg(flag, loadingFlag) { switch (flag) { case 'buffering': //console.log('finished buffering...'); if(loadingFlag && $("#buffer-loader").is(":visible")) { $("#ttsPause").css('display', 'inline-block'); $("#buffer-loader").css('display', 'none'); } $(readerWindow.document.body).find('.msg_buf').remove(); break; case 'no_network': //console.log('removing error!'); clearTimeout(errTimer); $(readerWindow.document.body).find('.msg_err').remove(); break; } } function getPlayState() { if (currAudio === null) return false; return !currAudio.paused } function setVolume(vol) { currAudio.volume = nextAudio.volume = vol; } function clear() { ttsParser = ttsHilite = undefined; hideMsg('buffering', true); } function speakText() { /* In case speak is called before TTS */ if (typeof ttsHilite === "undefined" || typeof ttsParser === "undefined") { ttsHilite = ttsHiliteModule(window); ttsParser = ttsParserModule(window); } speak = true; var speakNodes = document.getElementsByClassName(speakClass); angular.element(speakNodes).find('.nt-icon').remove(); reset(); ttsParser.setCurrNode(speakNodes[0]); ttsParser.setEndNode(speakNodes[speakNodes.length - 1].firstChild); //init(); play(); } var clearSpeak = function () { currTerminate = false; speak = false; ttsParser.setEndNode(null); Hilite.removeHilite(document.body, speakClass); //readerWindow.doGetBackHL(); }; document.addEventListener('DOMContentLoaded', function () { //init() }); function initpublic() { } /* exposing public functions */ function playNext(){ currAudio.pause(); endedHandler(); } function playPrevious(){ currAudio.pause(); removeHilite(); if (currTerminate) { stop(true); return } /* stop caching audio */ if (currTerminate && speak) { clearSpeak(); stop(); return } /* stop speak audio*/ retryCount = 1000; updateState(true); } function getCurrentAudio() { return currAudio; } return { init: initpublic, play: play, pause: pause, stop: stop, speakText: speakText, getPlayState: getPlayState, setVolume: setVolume, clear: clear, reset: reset, clickHandler: clickHandler, playNext: playNext, playPrevious: playPrevious, setVoice: setVoice, setPlaybackSpeed: setPlaybackSpeed, getCurrentAudio: getCurrentAudio, setScrollDirection: setScrollDirection } } function hideTTSPopup() { $("#toggleTtsIcon .ttsModal").hide(); } /* Depedencies 1. stop(): TtsStopped event triggered on
tag of the readerWindow */