{"name":"Passage reader","key":"textblockreader","version":"1.0.11","instructions":"This is a small html5 audio player that will read aloud the enclosed text block. There are text to speech options as well as the option to highlight words or sentences. Sentences works better. (Uses Cloud Poodll) ","showatto":"1","showplayers":"0","requirecss":"//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css","requirejs":"","shim":"","defaults":"highlightmode=\"sentence|word|none\",pause=\"fa-stop\",play=\"fa-volume-up\",stoporpause=\"stop|pause\", background=\"red\",color=\"#fff\",width=\"40\",height=\"40\",speaker=\"Male|Female\",language=\"English(US)|English(GB)|English(AU)|English(In)|English(Welsh)|Danish|Dutch|French(FR)|French(CA)|German|Icelandic|Italian|Japanese|Korean|Norwegian|Polish|Portugese(BR)|Portugese(PT)|Romanian|Russian|Spanish(ES)|Spanish(US)|Swedish|Turkish|Welsh\"","amd":"1","body":"\n
\n\n
","bodyend":"
\n
","script":"//now we need to ensure multiple passages so we wrap it all in a function and call it at the end.\n//start of instance wrapper\nvar passagereader = function(PASSAGEID){\n\n//DECLARATIONS and INITs ...........................\nvar thesentence_number =0;\nvar lettered= false;\n\n//audio player declarations\nvar aplayer = $('#' + PASSAGEID + '_player');\nvar fa = $('#' + PASSAGEID + ' .fa');\n\n//text to audio preparation\nvar format = \"text\";\n\n//determine the voice\nvar mf=@@speaker@@\nswitch(@@language@@){\ncase \"English(US)\": voice = mf=='Male'?'Joey':'Kendra';break;\ncase \"English(GB)\": voice = mf=='Male'?'Brian':'Amy';break;\ncase \"English(AU)\": voice = mf=='Male'?'Russell':'Nicole';break;\ncase \"English(IN)\": voice = mf=='Male'?'Aditi':'Raveena';break;\ncase \"English(WELSH)\": voice = mf=='Male'? 'Geraint':'Geraint';break;\ncase \"Danish\": voice = mf=='Male'?'Mads':'Naja';break;\ncase \"Dutch\": voice = mf=='Male'?'Ruben':'Lotte';break;\ncase \"French(FR)\": voice = mf=='Male'?'Mathieu':'Celine';break;\ncase \"French(CA)\": voice = mf=='Male'?'Chantal':'Chantal';break;\ncase \"German\": voice = mf=='Male'?'Hans':'Marlene';break;\ncase \"Icelandic\": voice = mf=='Male'?'Karl':'Dora';break;\ncase \"Italian\": voice = mf=='Male'?'Carla':'Giorgio';break;\ncase \"Japanese\": voice = mf=='Male'?'Takumi':'Mizuki';break;\ncase \"Korean\": voice = mf=='Male'?'Seoyan':'Seoyan';break;\ncase \"Norwegian\": voice = mf=='Male'?'Liv':'Liv';break;\ncase \"Polish\": voice = mf=='Male'?'Jacek':'Ewa';break;\ncase \"Portugese(BR)\": voice = mf=='Male'?'Ricardo':'Vitoria';break;\ncase \"Portugese(PT)\": voice = mf=='Male'?'Cristiano':'Ines';break;\ncase \"Romanian\": voice = mf=='Male'?'Carmen':'Carmen';break;\ncase \"Russian\": voice = mf=='Male'?'Maxim':'Tatyana';break;\ncase \"Spanish(ES)\": voice = mf=='Male'?'Enrique':'Conchita';break;\ncase \"Spanish(US)\": voice = mf=='Male'?'Miguel':'Penelope';break;\ncase \"Swedish\": voice = mf=='Male'?'Astrid':'Astrid';break;\ncase \"Turkish\": voice = mf=='Male'?'Filiz':'Filiz';break;\ncase \"Welsh\": voice = mf=='Male'?'Gwyneth':'Gwyneth';break;\ndefault: voice = mf=='Male'?'Brian':'Amy';\n}\n\n\n//fetch the text to read\nvar useblock = $('#' + PASSAGEID + '_textblock');\nvar usetext = useblock.text();\n\n//some common selectors\nvar wordselector = '#' + PASSAGEID+ '_textblock span.tbr_word';\nvar sentenceselector = '#' + PASSAGEID+ '_textblock span.tbr_sentence';\n\n//FUNCTIONS ...........................\n//FUNCTION fetch polly url\n var fetch_polly_url = function(speaktext, voice, callback) {\n\n //The REST API we are calling\n var functionname = 'local_cpapi_fetch_polly_url';\n\n //fetch the Posturl. We need this.\n //set up our ajax request\n var xhr = new XMLHttpRequest();\n var that = this;\n\n //set up our handler for the response\n xhr.onreadystatechange = function (e) {\n if (this.readyState === 4) {\n if (xhr.status == 200) {\n\n //get a yes or forgetit or tryagain\n var payload = xhr.responseText;\n var payloadobject = JSON.parse(payload);\n if (payloadobject) {\n //returnCode > 0 indicates an error\n if (payloadobject.returnCode > 0) {\n console.log(payloadobject.returnMessage);\n return false;\n //if all good, then lets do the embed\n } else if (payloadobject.returnCode === 0){\n var pollyurl = payloadobject.returnMessage;\n callback(pollyurl);\n } else {\n console.log('Polly Signed URL Request failed:');\n console.log(payloadobject);\n }\n } else {\n console.log('Polly Signed URL Request something bad happened');\n }\n } else {\n console.log('Polly Signed URL Request Not 200 response:' + xhr.status);\n }\n }\n };\n\n //make our request\n var xhrparams = \"wstoken=\" + @@CLOUDPOODLLTOKEN@@\n + \"&wsfunction=\" + functionname\n + \"&moodlewsrestformat=\" + 'json'\n + \"&text=\" + encodeURIComponent(speaktext)\n + '&texttype=text'\n + '&voice=' + voice\n + '&appid=' + 'filter_poodll'\n + '&owner=poodll'\n + '®ion=useast1';\n\n var serverurl = 'https://cloud.poodll.com' + \"/webservice/rest/server.php\";\n xhr.open(\"POST\", serverurl, true);\n xhr.setRequestHeader(\"Cache-Control\", \"no-cache\");\n xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\n xhr.send(xhrparams);\n };\n\n//FUNCTION: determine if the string is text or HTML\nvar isHTML = function (testString) {\n var htmlRegex = new RegExp(\"<([A-Za-z][A-Za-z0-9]*)\\\\b[^>]*>(.*?)\");\n return htmlRegex.test(testString);\n};\n\n//FUNCTION: split a text passage into words\nvar split_into_words= function(thetext){\n thetext = thetext.replace(/\\s+/g,' ').trim();\n if(thetext==''){return[]};\n return thetext.split(' '); \n};\n\n//FUNCTION: split a text passage into sentences\nvar split_into_sentences = function(thetext){\n thetext = thetext.replace(/\\s+/g,' ').trim();\n if(thetext ==''){return[]};\n return thetext.match(/([^\\.!\\?]+[\\.!\\?\"']+)|([^\\.!\\?\"']+$)/g); \n};\n\n//FUNCTION: break a text passage into words/sentences, and surround the words with marker tags\nvar spanify_text_passage = function(){\n //the itemcount er\n var itemcount = -1;\n\n //get all the text nodes in the useblock\n var textnodes = useblock.find('*').contents().filter(function(){ return this.nodeType == 3; });\n //wrap sentence or words in text block with spans\n textnodes.each(function(){\n var retpieces = ''; \n if(@@highlightmode@@=='word'){\n //for words\n var thewords = split_into_words($(this).text());\n for (var theword=0; theword < thewords.length; theword++){\n itemcount++;\n retpieces = retpieces + '' + thewords[theword] + ' ';\n }//end of for loop\n }else{\n //for sentences\n var thesentences = split_into_sentences($(this).text());\n for (var thesentence=0; thesentence < thesentences.length; thesentence++){\n itemcount++;\n retpieces = retpieces + '' + thesentences[thesentence] + ' ';\n }//end of for loop\n }\n $(this).replaceWith(retpieces);\n });//end of textnodes each\n};\n\n//FUNCTION: unhighlight a sentence as active\nvar dehighlight_all = function(){\n switch(@@highlightmode@@){\n case 'word':\n $(wordselector,useblock).removeClass('activesentence');\n break;\n case 'sentence':\n $(sentenceselector,useblock).removeClass('activesentence');\n break;\n case 'none':\n default:\n //do nothing\n }\n}\n\n//FUNCTION: highlight a sentence as active\nvar highlight_sentence = function(thesentence){\n switch(@@highlightmode@@){\n case 'word':\n $(wordselector,useblock).removeClass('activesentence');\n $(wordselector,useblock).slice(wordstarts[thesentence],\n wordstarts[thesentence] + \n wordcounts[thesentence]).addClass('activesentence');\n break;\n case 'sentence':\n $(sentenceselector).removeClass('activesentence');\n $(sentenceselector + '[data-sentenceindex=' + thesentence + ']').addClass('activesentence');\n break;\n case 'none':\n default:\n //do nothing\n }\n}\n\n//FUNCTION: play a single sentence and mark it active for display purposes\nvar doplayaudio = function(thesentence){\n highlight_sentence(thesentence);\n aplayer.attr('src',sentenceURLs[thesentence]);\n aplayer[0].play();\n};\n\n//AUDIO PLAYER events\naplayer[0].addEventListener('ended', function(){\n if(thesentence_number< sentences.length -1){\n thesentence_number++;\n doplayaudio(thesentence_number);\n }else{\n dehighlight_all();\n $(fa).removeClass(@@pause@@);\n $(fa).addClass(@@play@@);\n aplayer[0].pause();\n }\n});\n\n//handle audio player button clicks\n$('#' + PASSAGEID).click(function(){\n if(!aplayer[0].paused && !aplayer[0].ended){\n aplayer[0].pause();\n if(@@stoporpause@@=='stop'){\n aplayer[0].load();\n thesentence_number=0;\n }\n $(fa).removeClass(@@pause@@);\n $(fa).addClass(@@play@@);\n\n //if paused and in limbo no src state\n }else if(aplayer[0].paused && aplayer.attr('src')){\n aplayer[0].play();\n $(fa).removeClass(@@play@@);\n $(fa).addClass(@@pause@@);\n//play \n}else{\n if(!lettered){\n spanify_text_passage();\n lettered=true;\n };//end of if lettered\n if(@@stoporpause@@=='stop'){\n thesentence_number=0;\n } \n doplayaudio(thesentence_number);\n $(fa).removeClass(@@play@@);\n $(fa).addClass(@@pause@@);\n }//end of if paused ended\n});\n\n//handle sentence clicks\n$('#' + PASSAGEID + '_textblock .tbr_innerdiv').on('click', '.tbr_sentence',function(){\naplayer[0].pause();\n var sentenceindex = $(this).attr('data-sentenceindex');\n $(fa).removeClass(@@play@@);\n $(fa).addClass(@@pause@@);\n thesentence_number = sentenceindex; \n doplayaudio(sentenceindex );\n});\n\n//PROCEDURAL stuff ...........................\n//break it into sentences, and fetch data + TTS URL for each sentence\nvar sentences = split_into_sentences(usetext);\nvar wordstarts=[];\nvar wordcounts=[];\nvar sentenceURLs=[];\nvar previousend=0;\nfor (var currentsentence=0;currentsentence