{"name":"Dictation Chat","key":"dictationchat","version":"1.0.1","instructions":"Enter the sentences to be used for dictation as an HTML unordered list (bullet points) between the tags.","showatto":"1","showplayers":"0","requirecss":"https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.min.css","requirejs":"","shim":"","defaults":"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\",speaker=\"Male|Female\"comparison=simple|advanced,checkcase=true|false","amd":"1","body":"<!-- @@CLOUDPOODLLTOKEN@@ -->\n\n<div id=\"dictate_loading\">\n    <i class=\"fa fa-spinner fa-spin\"></i> Loading\n</div>\n\n<div id=\"dictate_container\" style=\"display:none\">\n    \n<div style=\"display:none;\" id=\"dictate_results\">RESULTS</div>\n\n<div id=\"dictate_title\" class=\"text-center\">Listen and Type</div>\n\n<div id=\"dictate_mainmenu\">\n    <p>Listen and type the sentences spoken by the computer.</p>\n    <p>Click 'Start' to begin the game!</p>\n</div>\n\n<div style=\"display:none;\" id=\"dictate_game\"></div>\n\n<button class='btn btn-block btn-primary' id=\"dictate_start_btn\">\n    <i class='fa fa-play'></i> Start\n</button>\n\n<div style=\"display:none;\" id=\"dictate_controls\">\n    \n    <button class=\"btn btn-secondary ctrl-btn\" id=\"dictate_listen_btn\">\n        <i class=\"fa fa-volume-up\"></i> Listen\n    </button>\n    \n    <button class=\"btn btn-success ctrl-btn\" id=\"dictate_check_btn\" >\n        <i class=\"fa fa-check\"></i> Check\n    </button>\n    \n    \n    <button class=\"btn btn-danger ctrl-btn\" id=\"dictate_skip_btn\">\n        Skip <i class=\"fa fa-arrow-right\"></i>\n    </button>\n\n</div>","bodyend":"</div>\n\n<div style=\"display:none;\" class=\"alert alert-danger\" id=\"landr_error\"></div>","script":"var app = {\n    errors:{\n        \"no_text\":\"No text items found! Ensure you add your text items between <li> tags!\"\n    },\n    spliton: new RegExp('([,.!?:;\" ])','g'),\n    game:{\n        pointer:0\n    },\n    usevoice: 'Amy',\n   \n    setvoice: function(){\n      //determine voice\n      var mf=@@speaker@@;\n      var voice='Amy';\n      switch(@@language@@){\n           case \"English(US)\": voice = mf=='Male'?'Joey':'Kendra';break;\n           case \"English(GB)\": voice = mf=='Male'?'Brian':'Amy';break;\n           case \"English(AU)\": voice = mf=='Male'?'Russell':'Nicole';break;\n           case \"English(IN)\": voice = mf=='Male'?'Aditi':'Raveena';break;\n           case \"English(Welsh)\": voice = mf=='Male'? 'Geraint':'Geraint';break;\n           case \"Danish\": voice = mf=='Male'?'Mads':'Naja';break;\n           case \"Dutch\": voice = mf=='Male'?'Ruben':'Lotte';break;\n           case \"French(FR)\": voice = mf=='Male'?'Mathieu':'Celine';break;\n           case \"French(CA)\": voice = mf=='Male'?'Chantal':'Chantal';break;\n           case \"German\": voice = mf=='Male'?'Hans':'Marlene';break;\n           case \"Icelandic\": voice = mf=='Male'?'Karl':'Dora';break;\n           case \"Italian\": voice = mf=='Male'?'Carla':'Giorgio';break;\n           case \"Japanese\": voice = mf=='Male'?'Takumi':'Mizuki';break;\n           case \"Korean\": voice = mf=='Male'?'Seoyan':'Seoyan';break;\n           case \"Norwegian\": voice = mf=='Male'?'Liv':'Liv';break;\n           case \"Polish\": voice = mf=='Male'?'Jacek':'Ewa';break;\n           case \"Portugese(BR)\": voice = mf=='Male'?'Ricardo':'Vitoria';break;\n           case \"Portugese(PT)\": voice = mf=='Male'?'Cristiano':'Ines';break;\n           case \"Romanian\": voice = mf=='Male'?'Carmen':'Carmen';break;\n           case \"Russian\": voice = mf=='Male'?'Maxim':'Tatyana';break;\n           case \"Spanish(ES)\": voice = mf=='Male'?'Enrique':'Conchita';break;\n           case \"Spanish(US)\": voice = mf=='Male'?'Miguel':'Penelope';break;\n           case \"Swedish\": voice = mf=='Male'?'Astrid':'Astrid';break;\n           case \"Turkish\": voice = mf=='Male'?'Filiz':'Filiz';break;\n           case \"Welsh\": voice = mf=='Male'?'Gwyneth':'Gwyneth';break;\n           default: voice = mf=='Male'?'Brian':'Amy';\n      }\n      this.usevoice=voice;\n   },\n\n    preloadAudio:function(){\n        \n        var self=this;\n        var fullyLoaded = false;\n        \n        console.log(\"Preloading audio..\");\n        \n        self.items.forEach(function(item,idx){\n           \n           self.getTTS(item.target,self.usevoice,function(pollyurl){\n            \n                item.audio = new Audio();\n                item.audio.src=pollyurl;\n    \n                fullyLoaded = self.items.filter(function(item){\n                    return item.audio === null\n                }).length === 0;\n                \n                if(fullyLoaded){\n                    self.appReady();\n                }\n            });\n            \n        });\n    },\n    getItems:function(){\n        \n        var self=this;\n        var text_items = [];\n        \n        console.log(\"Getting items..\");\n        \n        $(\"#dictate_container li\").each(function(){\n            text_items.push($(this).text().trim());\n        });\n        \n        this.items = text_items.map(function(target){\n            return {\n                targetWords:target.split(self.spliton).filter(function(e){return e!==\"\";}),\n                target:target,\n                typed:\"\",\n                answered:false,\n                correct:false,\n                audio:null\n            };\n        }).filter(function(e){return e.target!==\"\";});\n\n        console.log(this.items);\n\n        if(!this.items.length){\n            $(\"#dictate_error\").text(this.errors.no_text).show();\n        }\n        \n        else{\n            self.preloadAudio();\n        }\n        \n    },\n    init:function(){\n\n        window.app = this;\n        var self = this;\n        \n        console.log(\"Loading libraries..\");\n        \n        require(['jquery', 'core/ajax'], function($, Ajax) {\n            self.ajax = Ajax;\n            self.setvoice();\n            self.getItems();\n        });\n        \n    },\n    appReady:function(){\n        console.log(\"App ready!\");\n        $(\"#dictate_container li\").remove();\n        this.attachHandlers();\n        $(\"#dictate_loading\").hide();\n        $(\"#dictate_container\").show();\n    },\n    gotComparison: function(comparison,typed){\n        \n        var self = this;\n        \n        $(\".targetWord\").addClass(\"correct\").removeClass(\"incorrect\");\n        \n        if(!Object.keys(comparison).length){\n            //add success marker here if wish\n            $(\".speech.teacher_left\").text(self.items[self.game.pointer].target+\"\");\n\n            self.items[self.game.pointer].answered=true;\n            self.items[self.game.pointer].correct=true;\n            self.items[self.game.pointer].typed=typed;\n            \n            if(self.game.pointer<self.items.length-1){\n                setTimeout(function(){\n                    self.game.pointer++;\n                    self.nextPrompt();\n                },5000);\n            }\n            \n            else{\n                setTimeout(function(){\n                    self.end();\n                },5000);\n            }\n            \n        }\n        \n        else{\n            \n            Object.keys(comparison).forEach(function(idx){\n                $(\".targetWord[data-idx='\"+idx+\"']\").removeClass(\"correct\").addClass(\"incorrect\");\n            })\n            \n            $(\"#dictate_reply_\"+self.game.pointer).effect(\"shake\",function(){\n                \n                $(\".ctrl-btn\").prop(\"disabled\",false);\n                \n            });\n            \n        }\n        \n        $(\".targetWord.correct\").each(function(){\n            var realidx = $(this).data(\"realidx\");\n            var targetWord = self.items[self.game.pointer].targetWords[realidx];\n            $(this).val(targetWord).prop(\"disabled\",true);\n        });\n        \n    },\n\n   getWords: function(thetext){\n       if(@@checkcase@@=='false'){thetext = thetext.toLowerCase();}\n       var chunks = thetext.split(this.spliton).filter(function(e){return e!==\"\";});\n       var words=[];\n         for(var i=0;i<chunks.length;i++){\n             if(!chunks[i].match(this.spliton)){\n                 words.push(chunks[i]);  \n             }\n         }\n       return words;\n    },\n\n    getSimpleComparison(passage,transcript,callback){\n       var pwords = this.getWords(passage);\n       var twords = this.getWords(transcript);\nconsole.log(\"passage\",pwords);\nconsole.log(\"transcript\",twords);\n       var ret ={};\n       for(var pi=0;pi<pwords.length && pi<twords.length;pi++){\n               if(pwords[pi]!=twords[pi]){\n                  ret[pi+1] = {\"word\": pwords[pi],\"number\": pi+1};\n               }\n        }\n        callback(ret);\n   },\n\n    getComparison: function(passage,transcript,callback){\n\n       if(@@comparison@@=='simple'){\n          this.getSimpleComparison(passage,transcript,callback);\n           return; \n       }\n   \n        console.log(\"getting comparison..\");\n        $(\".ctrl-btn\").prop(\"disabled\",true);\n\n        this.ajax.call([{\n            methodname: 'mod_readaloud_compare_passage_to_transcript',\n            args: {\n                passage: passage,\n                transcript: transcript,\n                alternatives: '',\n                language: 'en-US'\n            },\n            done:function(ajaxresult){\n                console.log(\"got comparison!\");\n                var payloadobject = JSON.parse(ajaxresult);\n                if (payloadobject) {\n                    callback(payloadobject);\n                } else{\n                    callback(false);\n                }\n            },\n            fail:function(err){\n                console.log(err);\n            }\n        }]);\n        \n    },\n    end:function(){\n        var numCorrect = this.items.filter(function(e){return e.correct;}).length;\n        var totalNum = this.items.length;\n        $(\"#dictate_results\").html(\"TOTAL<br/>\"+numCorrect+\"/\"+totalNum).show();\n        this.animateCSS(\"#dictate_results\", \"tada\", function(){\n            setTimeout(function(){\n                $(\"#dictate_results\").fadeOut();\n            },2000);\n        });\n        $(\"#dictate_game\").hide();\n        $(\"#dictate_start_btn\").show();\n        $(\"#dictate_mainmenu\").show();\n        $(\"#dictate_controls\").hide();\n        $(\"#dictate_title\").html(\"Listen and Repeat\");\n    },\n    start:function(){\n        this.items.forEach(function(item){\n            item.spoken=\"\";\n            item.answered=false;\n            item.correct=false;\n        });\n        this.game.pointer=0;\n        $(\"#dictate_game\").show();\n        $(\"#dictate_start_btn\").hide();\n        $(\"#dictate_mainmenu\").hide();\n        $(\"#dictate_controls\").show();\n        this.nextPrompt();\n    },\n    nextPrompt:function(){\n        \n        var self = this;\n\n        var target = this.items[this.game.pointer].target;\n        var id = \"dictate_prompt_\"+this.game.pointer;\n        var code=\"<div class='dictate_prompt' style='display:none;' id='\"+id+\"'>\";\n        \n        code+=\"<i class='fa fa-graduation-cap speech-icon-left'></i>\";\n        code+=\"<div style='margin-left:90px;' class='speech teacher_left'>\";\n        code+=target.replace(/[^a-zA-Z0-9 ]/g,'').replace(/[a-zA-Z0-9]/g,'•');\n        code+=\"</div>\";\n        code+=\"</div>\";\n        \n        $(\"#dictate_game\").html(code);\n        $(\".ctrl-btn\").prop(\"disabled\",false);\n        \n        var color;\n        \n        var progress = self.items.map(function(item,idx){\n            color = \"gray\";\n            if(self.items[idx].answered && self.items[idx].correct){\n                color=\"green\";\n            }\n            else if(self.items[idx].answered && !self.items[idx].correct){\n                color=\"red\";\n            }\n            return \"<i style='color:\"+color+\"' class='fa fa-circle'></i>\";\n        }).join(\" \");\n        \n        $(\"#dictate_title\").html(progress);\n        $(\"#\"+id).toggle(\"slide\",{direction:'left'});\n        \n        self.nextReply();\n        \n    }, \n   nextReply:function(){\n        var self = this;\n        var id = \"dictate_reply_\"+this.game.pointer;\n        var target = this.items[this.game.pointer].target;\n        var code=\"<div class='dictate_reply' style='display:none;' id='\"+id+\"'>\";\n        code+=\"<i class='fa fa-user speech-icon-right'></i>\";\n        var targetWordsCode = \"\";\n        var idx = 1;\n        self.items[self.game.pointer].targetWords.forEach(function(word,realidx){\n            if(!word.match(self.spliton)){\n                targetWordsCode+=\"<input type='text' maxlength='\"+word.length+\"' size='\"+(word.length+1)+\"' class='targetWord' data-realidx='\"+realidx+\"' data-idx='\"+idx+\"'>\";\n                idx++;\n                \n            } else {\n                targetWordsCode+=word;\n            }\n        });\n        code+=\"<div style='margin-right:90px;' class='speech right'>\"+targetWordsCode+\"</div>\";\n        code+=\"</div>\";\n        $(\"#dictate_game\").append(code);\n        $(\"#\"+id).toggle(\"slide\",{direction:'right'});\n    },\n    getTTS: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        + '&region=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    render:function(){\n        var self = this;\n    },\n    attachHandlers:function(){\n        var self = this;\n        $(\"#dictate_start_btn\").on(\"click\",function(){\n            self.start();\n        });\n        $(\"#dictate_listen_btn\").on(\"click\",function(){\n            self.items[self.game.pointer].audio.load();\n            self.items[self.game.pointer].audio.play();\n        });\n        $(\"#dictate_skip_btn\").on(\"click\",function(){\n           if(self.game.pointer<self.items.length-1){\n                self.items[self.game.pointer].answered=true;\n                self.items[self.game.pointer].correct=false;\n                self.game.pointer++;\n                self.nextPrompt();\n            }\n            else{\n                self.end();\n            }\n        });\n        $(\"#dictate_check_btn\").on(\"click\",function(){\n            \n            var passage = self.items[self.game.pointer].target;\n            var transcriptArray = [];\n            \n            $(\".targetWord\").each(function(){\n                transcriptArray.push($(this).val());\n            })\n            \n            var transcript = transcriptArray.join(\" \");\n            \n            console.log(passage,transcript);\n            \n            self.getComparison(passage,transcript,function(comparison){\n                console.log(comparison);\n                self.gotComparison(comparison,transcript);\n            })\n            \n        })\n    },\n    animateCSS:function(element, animationName, callback) {\n        const node = document.querySelector(element)\n        node.classList.add('animated', animationName)\n    \n        function handleAnimationEnd() {\n            node.classList.remove('animated', animationName)\n            node.removeEventListener('animationend', handleAnimationEnd)\n    \n            if (typeof callback === 'function') callback()\n        }\n    \n        node.addEventListener('animationend', handleAnimationEnd)\n    }\n}\n\napp.init();\n","style":"#dictate_container{\n    border: 1px solid darkgray;\n    position: relative;\n    height: 550px;\n    overflow: hidden;\n    max-width: 400px;\n    margin: auto;\n}\n.dictate_prompt{\n    position:relative;\n    margin-top:10px;\n}\n.dictate_reply{\n    margin-top:10px;\n    position:relative;\n}\n#dictate_error{\n}\n#dictate_game{\n    height:460px;\n    padding:10px;\n    border-top:1px solid darkgray;\n    border-bottom:1px solid darkgray;\n    background-color: rgba(98,135,182,1);\n}\n.speech {\n  position: relative;\n  font-family: arial;\n  font-size: 16px;\n  background: #a53d38;\n  color: black;\n  border-radius: 10px;\n  padding: 20px;\n}\n\n#dictate_container .right{\n    background-color: white;\n    \n}\n#dictate_container .teacher_left{\n    background-color: rgba(122,221,76,1);\n}\n\n#dictate_container .teacher_left:after {\n    content: '';\n    border: 20px solid transparent;\n    border-right-color: rgba(122,221,76,1);\n    border-left: 0;\n    position: absolute;\n    left: -20px;\n    top: 50%;\n    margin-top: -20px;\n}\n\n#dictate_container .right:after {\n    content: '';\n    border: 20px solid transparent;\n    border-left-color: white;\n    border-right: 0;\n    position: absolute;\n    right: -20px;\n    top: 50%;\n    margin-top: -20px;\n}\n\n.speech-icon-left{\n    position: absolute;\n    left: 0;\n    top: 0;\n    bottom: 0;\n    margin: auto;\n    font-size: 50px;\n    height:50px;\n}\n\n.speech-icon-right{\n    position: absolute;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    margin: auto;\n    font-size: 50px;\n    height:50px;\n}\n\n#dictate_mainmenu{\n    height: 460px;\n    background-color: #f5f5f5;\n    font-size: 20px;\n    padding: 10px;\n    border-top: 1px solid darkgray;\n    border-bottom: 1px solid darkgray;\n}\n\n#dictate_title{\n    border: 2px solid darkgray;\n    height: 53px;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    font-size: 24px;\n    font-weight: bold;\n}\n\n#dictate_controls{\n    position:absolute;\n    left:0;\n    right:0;\n    bottom:0;\n    height:35px;\n    background-color:white;\n}\n\n#dictate_results{\n    position:absolute;\n    left:0;\n    right:0;\n    bottom:0;\n    top:0;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    font-size:75px;\n    background-color:rgba(98,135,182,1);\n    font-weight:bold;\n    color:white;\n    text-align:center;\n}\n\n#dictate_container .correct{\n    background-color: lightgreen;\n}\n\n#dictate_container .incorrect{\n    background-color: lightpink;\n}\n\n.targetWord{\n    padding:3px;\n    border-radius:5px;\n    margin:5px;\n}\n\n#dictate_container .ctrl-btn{\n    width:33.33%;\n    float:left;\n}\n\n#dictate_loading{\n    font-size:30px;\n    text-align:center;\n    width:100%;\n    height:50px;\n    border:1px solid black;\n}","dataset":"","datasetvars":"","alternate":"","alternateend":""}