<commit_before><!DOCTYPE html>
<html lang="en">
<!-- ~~~~~~~~~ WARNING: HACKY WEEKEND CODING AHEAD ~~~~~~~~~ -->
  <head>
    <meta charset="utf-8">
    <title>~ ~ ~ ~ T O B I A S ~ ~ ~ ~</title>
    <style>
      body {
        margin:0px;
        padding:50px;
        height:100%;
        background: url(img/planets.jpg) repeat 0 0;
        -webkit-animation:bgscroll 150s infinite linear;
        font-family: sans-serif;
        overflow: hidden;
      }

      h2 {
        padding:0px;
        margin:0px 0px 40px 0px;
        font-family: 'SuastOrnad tfb', sans-serif;
        font-size: 96px;
        font-weight: normal;
      }

      h3 {
        padding:0px;
        margin:0px 0px 10px 0px;
        font-family: 'SuastOrnad tfb', sans-serif;
        font-size: 18px;
        font-weight: normal;
      }

      #container-element {
        margin:0px;
        color: #fff;
        
        height:100%;
      }


      #startBtn {
        background-color: #0A68FF;
        padding: 15px 25px 15px 25px;
        border: none;
        border-radius: 8px;
        color: #fff;
        font-size: 20px;
        margin-right: 20px;
      }

      #startBtn:hover {
        background-color: #0342A6;
        cursor: pointer;
      }

      #stopBtn {
        background-color: #F58C31;
        padding: 15px 25px 15px 25px;
        border: none;
        border-radius: 8px;
        color: #fff;
        font-size: 20px;
        margin-right: 20px;
      }

      #stopBtn:hover {
        background-color: #B51414;
        cursor: pointer;
      }

      #status {
        position: absolute;
        bottom: 50px;
        font-size: 12px;
        line-height: 20px
      }

      .giphy {
        position: absolute;
        right: -500px;
        top: 0px;
        -webkit-animation:gifscroll 20s linear;
        width: 500px;
      }

      #output {
        font-size: 60px;
        color: #2AAB13;
        font-weight: bold;
        height:150px;overflow:auto;
        font-family: 'Adobe Caslon Pro', 'Vipond Octic';
        font-style: italic;
      }

      #saybox {
        margin-top:70px;
      }

      #recording-indicator {
        border-radius: 10px; 
        -moz-border-radius: 10px; 
        -webkit-border-radius: 10px; 
        width: 20px; 
        height: 20px; 
        background: #ff0000;
        -webkit-animation:recording 1s linear infinite;
      }

      @-webkit-keyframes bgscroll {
        from {background-position:0 1800px;}
        to {background-position:0 0px;}
      }   

      @-webkit-keyframes gifscroll {
        0% {right:-500px; top:0px;}
        2% {right: 50px; top:0px;}
        100% {right: 200px; top:2000px;}
      }   

      @-webkit-keyframes gifscrolls {
        from {right:0px;}
        to {right: 200px;}
      }    

      @-webkit-keyframes recording {
        0% {opacity: 0;}
        50% {opacity: 1;}
        100% {opacity: 0;}
      }    

    </style>
  </head>
  <body>
    <div id="container-element">
      <h2>join us for a story</h2>
     
      <button id="startBtn">Begin</button>
      <button id="stopBtn">End</button>
      <span id="recording-indicator"></span>
      <div id="saybox">
        <!--<p>What you say?</p>-->
        <div id="output">
        </div>
      </div>
      <div id="status">
        <h3>Status</h3>
        <div id="current-status">Loading page...</div>
      </div>
    </div>

    <script src="js/jquery.js"></script>
    <script src="js/wordlist.js"></script>

    <script>
      /*
      ~~~~~~~~~ WARNING: HACKY WEEKEND CODING AHEAD ~~~~~~~~~
      ~~~~~~~~~~~~~~~ PROCEED AT YOUR OWN RISK ~~~~~~~~~~~~~~
      */

      var prevWord = '';
      var keywordIds = [];

      // these are the words we wanna listen for mmkay
      var keynouns = "Contest,Arguing,Lost,Escaping,Sleeping,Found,Fighting,Love,Journey,Mischief,Alive,Hurt,Meeting,Storm,Imprisoned,Planning,Time,Breaks,Parting,Transforming,Rescued,Revealed,Dying,Food,Axe,Returned,Ring,Clothes,Book,Sword,Door,Cauldron,Trap,Stairs,Fire,Key,Tree,Spell,Crown,Wall,Well,Treasure,Window,Gift,Beautiful,Foolish,Cursed,Brave,Frightened,Poisoned,Disguised,Happy,Fly,Hidden,Lucky,Talk,Healed,Sad,Tiny,Stolen,Ugly,Wicked,Wise,Strong,Faraway,Secret,Blind,lost,City,Crazy,Cave,Dungeon,Cottage,Sky,Garden,Forest,Mountain,Palace,Kitchen,Night,Road,River,Swamp,Tower,Village,Kingdom,Church,Home,Ruin,King,Beggar,Fairy,Prince,Brother,Sister,Giant,Princess,Child,Wolf,Page,Queen,Stepmother,Frog,Guard,Parent,Horse,Cook,Husband,Wife,Monster,Enemy,Old,Dragon,Thief,Orphan,Witch,GARB_AA,GARB_AE,GARB_AH,GARB_AO,GARB_AW,GARB_AY,GARB_B,GARB_CH,GARB_D,GARB_DH,GARB_EH,GARB_ER,GARB_EY,GARB_F,GARB_G,GARB_HH,GARB_IH,GARB_IY,GARB_JH,GARB_K,GARB_L,GARB_M,GARB_N,GARB_NG,GARB_OW,GARB_OY,GARB_P,GARB_R,GARB_S,GARB_SH,GARB_T,GARB_TH,GARB_UH,GARB_UW,GARB_V,GARB_W,GARB_Y,GARB_Z,GARB_ZH".toUpperCase();

      var keynounsArr = keynouns.split(",");
      var prob = -999999;

      // add the keywords to the grammar in the correct gramma object format
      var grammarKeywords = {numStates: 1, start: 0, end: 0, transitions: []};
      for (var i = 0; i < keynounsArr.length; i++) {
        if (keynounsArr[i].substr(0,4) === "GARB") {
          prob = -777777;
        }
        grammarKeywords.transitions.push({from: 0, to: 0, logp: prob, word: keynounsArr[i]});
      };

      // These will be initialized later
      var recognizer, recorder, callbackManager, audioContext, outputContainer;
      // Only when both recorder and recognizer do we have a ready application
      var recorderReady = recognizerReady = false;

      // A convenience function to post a message to the recognizer and associate
      // a callback to its response
      function postRecognizerJob(message, callback) {
        var msg = message || {};
        if (callbackManager) msg.callbackId = callbackManager.add(callback);
        if (recognizer) recognizer.postMessage(msg);
      };

      // This function initializes an instance of the recorder
      // it posts a message right away and calls onReady when it
      // is ready so that onmessage can be properly set
      function spawnWorker(workerURL, onReady) {
          recognizer = new Worker(workerURL);
          recognizer.onmessage = function(event) {
            onReady(recognizer);
          };
          recognizer.postMessage('');
      };

      // To display the hypothesis sent by the recognizer
      function updateCount(count) {
        console.log("Got count " + count);
        if (outputContainer) outputContainer.innerHTML = count;
      };

      // This updates the UI when the app might get ready
      // Only when both recorder and recognizer are ready do we enable the buttons
      function updateUI() {
        if (recorderReady && recognizerReady) startBtn.disabled = stopBtn.disabled = false;
      };

      // This is just a logging window where we display the status
      function updateStatus(newStatus) {
        document.getElementById('current-status').innerHTML += "<br/>" + newStatus;
      };

      // A not-so-great recording indicator
      function displayRecording(display) {
        if (display) document.getElementById('recording-indicator').innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
        else document.getElementById('recording-indicator').innerHTML = "";
      };

      // Callback function once the user authorises access to the microphone
      // in it, we instanciate the recorder
      function startUserMedia(stream) {
        var input = audioContext.createMediaStreamSource(stream);
        // Firefox hack https://support.mozilla.org/en-US/questions/984179
        window.firefox_audio_hack = input; 
        var audioRecorderConfig = {errorCallback: function(x) {updateStatus("Error from recorder: " + x);}};
        recorder = new AudioRecorder(input, audioRecorderConfig);
        // If a recognizer is ready, we pass it to the recorder
        if (recognizer) recorder.consumers = [recognizer];
        recorderReady = true;
        updateUI();
        updateStatus("Audio recorder ready!");
      };

      // This starts recording. We first need to get the id of the keyword search to use
      var startRecording = function() {
        //var id = document.getElementById('keyword').value;
        if (recorder && recorder.start()) displayRecording(true);
      };

      // Stops recording
      var stopRecording = function() {
        recorder && recorder.stop();
        displayRecording(false);
      };

      // Called once the recognizer is ready
      // We then add the grammars to the input select tag and update the UI
      var recognizerReady = function() {
           recognizerReady = true;
           updateUI();
           updateStatus("Recogniser ready!");
      };

      // load our grammar object into pocketsphinx
      var feedGrammar = function(grammarKeywords, index, id) {
        postRecognizerJob({command: 'addGrammar', data: grammarKeywords});
        recognizerReady();
      };

      // To display the hypothesis sent by the recognizer on the webpage
      function updateHyp(hyp) {
        if (outputContainer) outputContainer.innerHTML = hyp.toLowerCase();
      };

      // get a gif of the last word spoken
      function getGif(lastword) {
      
      // placeholder when no internet
      // var gif = "img/jack.jpg";
      // $('#container-element').append("<img class='giphy' src='" + gif + "'/>");

      $.getJSON("http://api.giphy.com/v1/gifs/search?q="+lastword+"&api_key=<KEY>", function(data) {
        var ran = Math.floor(Math.random(0,data.data.length-1));
        //var giftype = "fixed_width_downsampled";
        var giftype = "fixed_height_still";
        var gif = data.data[ran].images[giftype].url;
        var img = $('body').append("<img class='giphy' src='" + gif + "'/>");
     });
    }

      // This adds words (from wordList.js) to the recognizer. When it calls back, we add grammars
      var feedWords = function(words) {
           postRecognizerJob({command: 'addWords', data: words},
                        function() {feedGrammar(grammarKeywords, 0);});
      };

      // This initializes the recognizer. When it calls back, we add words
      var initRecognizer = function() {
          // You can pass parameters to the recognizer, such as : {command: 'initialize', data: [["-hmm", "my_model"], ["-fwdflat", "no"]]}
          postRecognizerJob({command: 'initialize', data: [["-remove_noise", "yes"]]},
                            function() {
                                        if (recorder) recorder.consumers = [recognizer];
                                        // feed words from the wordList.js file
                                        feedWords(wordList);});
      };

      // When the page is loaded, we spawn a new recognizer worker and call getUserMedia to
      // request access to the microphone
      window.onload = function() {
        outputContainer = document.getElementById("output");
        updateStatus("Recogniser init, asking for microphone access...");
        callbackManager = new CallbackManager();
        spawnWorker("js/recognizer.js", function(worker) {
          // This is the onmessage function, once the worker is fully loaded
          worker.onmessage = function(e) {

            // This is the case when we have a callback id to be called
            if (e.data.hasOwnProperty('id')) {
              var clb = callbackManager.get(e.data['id']);
              var data = {};
              if ( e.data.hasOwnProperty('data')) data = e.data.data;
              if(clb) clb(data);
            }
            // This is a case when the recognizer has a new hypothesis
            if (e.data.hasOwnProperty('hyp')) {
              var newHyp = e.data.hyp;
              // put all words in hypothesis string into an array for use
              var lastsp = newHyp.split(' ');
              var lastspLen = lastsp.length;

              // this gets the last word spoken in the concatenated hypothesis
              var lastword = lastsp.pop();

              // was the last word garbage of background noise?
              var garbage = (lastword.substr(0,4) === "GARB") ? true : false;

              // if the word is not a duplicate, blank, or garbage, then grab a GIF!
              if (lastword !== prevWord && lastword !== '' && !garbage) {
                getGif(lastword);
              }
              
              if (e.data.hasOwnProperty('final') &&  e.data.final) newHyp = "Final: " + newHyp;

              // if there's background noise, suggest to the user that this is so.
              if (garbage) { lastword = "* random noise *" };

              // send the last word recognised to the UI
              updateHyp(lastword);

              prevWord = lastword;

            }
            // This is the case when we have an error
            if (e.data.hasOwnProperty('status') && (e.data.status == "error")) {
              updateStatus("Error in " + e.data.command + " with code " + e.data.code);
            }

          };
          // Once the worker is fully loaded, we can call the initialize function
          initRecognizer();
        });

        // The following is to initialize Web Audio
        try {
          window.AudioContext = window.AudioContext || window.webkitAudioContext;
          navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
          window.URL = window.URL || window.webkitURL;
          audioContext = new AudioContext();
        } catch (e) {
          updateStatus("Error initializing Web Audio browser");
        }

        if (navigator.getUserMedia) {
          navigator.getUserMedia({audio: true}, startUserMedia, function(e) { updateStatus("No live audio input in this browser"); });
        } else {
          updateStatus("No web audio support in this browser");
        }

        // Wiring JavaScript to the UI
        var startBtn = document.getElementById('startBtn');
        var stopBtn = document.getElementById('stopBtn');
        startBtn.disabled = true;
        stopBtn.disabled = true;
        startBtn.onclick = startRecording;
        stopBtn.onclick = stopRecording;
      };
        
    </script>

    <script src="js/audioRecorder.js"></script>
    <script src="js/callbackManager.js"></script>
    
  </body>
</html>
<commit_msg>moar tweaking of css<commit_after><!DOCTYPE html>
<html lang="en">
<!-- ~~~~~~~~~ WARNING: HACKY WEEKEND CODING AHEAD ~~~~~~~~~ -->
  <head>
    <meta charset="utf-8">
    <title>~ ~ ~ ~ T O B I A S ~ ~ ~ ~</title>
    <style>
      body {
        margin:0px;
        padding:50px;
        height:100%;
        background: url(img/planets.jpg) repeat 0 0;
        -webkit-animation: bgscroll 150s infinite linear;
        font-family: sans-serif;
        overflow: hidden;
      }

      h2 {
        padding:0px;
        margin:0px 0px 40px 0px;
        font-family: 'SuastOrnad tfb', sans-serif;
        font-size: 96px;
        font-weight: normal;
      }

      h3 {
        padding:0px;
        margin:0px 0px 10px 0px;
        font-family: 'SuastOrnad tfb', sans-serif;
        font-size: 18px;
        font-weight: normal;
      }

      #container-element {
        margin:0px;
        color: #fff;
        
        height:100%;
      }


      #startBtn {
        background-color: #0A68FF;
        padding: 15px 25px 15px 25px;
        border: none;
        border-radius: 8px;
        color: #fff;
        font-size: 20px;
        margin-right: 20px;
      }

      #startBtn:hover {
        background-color: #0342A6;
        cursor: pointer;
      }

      #stopBtn {
        background-color: #F58C31;
        padding: 15px 25px 15px 25px;
        border: none;
        border-radius: 8px;
        color: #fff;
        font-size: 20px;
        margin-right: 20px;
      }

      #stopBtn:hover {
        background-color: #B51414;
        cursor: pointer;
      }

      #status {
        position: absolute;
        bottom: 50px;
        font-size: 12px;
        line-height: 20px
      }

      .giphy {
        position: absolute;
        right: -500px;
        top: 0px;
        -webkit-animation: gifscroll 20s linear;
        width: 500px;
      }

      #output {
        font-size: 60px;
        color: #2AAB13;
        font-weight: bold;
        height:150px;overflow:auto;
        font-family: 'Adobe Caslon Pro', 'Vipond Octic';
        font-style: italic;
      }

      #saybox {
        margin-top: 70px;
      }

      #recording-indicator {
        border-radius: 10px; 
        -moz-border-radius: 10px; 
        -webkit-border-radius: 10px; 
        width: 20px; 
        height: 20px; 
        background: #ff0000;
        -webkit-animation: recording 1s linear infinite;
      }

      @-webkit-keyframes bgscroll {
        from {background-position:0 1800px;}
        to {background-position:0 0px;}
      }   

      @-webkit-keyframes gifscroll {
        0% {right:-500px; top:0px;}
        2% {right: 50px; top:0px;}
        100% {right: 200px; top:2000px;}
      }   

      @-webkit-keyframes gifscrolls {
        from {right:0px;}
        to {right: 200px;}
      }    

      @-webkit-keyframes recording {
        0% {opacity: 0;}
        50% {opacity: 1;}
        100% {opacity: 0;}
      }    

    </style>
  </head>
  <body>
    <div id="container-element">
      <h2>join us for a story</h2>
     
      <button id="startBtn">Begin</button>
      <button id="stopBtn">End</button>
      <span id="recording-indicator"></span>
      <div id="saybox">
        <!--<p>What you say?</p>-->
        <div id="output">
        </div>
      </div>
      <div id="status">
        <h3>Status</h3>
        <div id="current-status">Loading page...</div>
      </div>
    </div>

    <script src="js/jquery.js"></script>
    <script src="js/wordlist.js"></script>

    <script>
      /*
      ~~~~~~~~~ WARNING: HACKY WEEKEND CODING AHEAD ~~~~~~~~~
      ~~~~~~~~~~~~~~~ PROCEED AT YOUR OWN RISK ~~~~~~~~~~~~~~
      */

      var prevWord = '';
      var keywordIds = [];

      // these are the words we wanna listen for mmkay
      var keynouns = "Contest,Arguing,Lost,Escaping,Sleeping,Found,Fighting,Love,Journey,Mischief,Alive,Hurt,Meeting,Storm,Imprisoned,Planning,Time,Breaks,Parting,Transforming,Rescued,Revealed,Dying,Food,Axe,Returned,Ring,Clothes,Book,Sword,Door,Cauldron,Trap,Stairs,Fire,Key,Tree,Spell,Crown,Wall,Well,Treasure,Window,Gift,Beautiful,Foolish,Cursed,Brave,Frightened,Poisoned,Disguised,Happy,Fly,Hidden,Lucky,Talk,Healed,Sad,Tiny,Stolen,Ugly,Wicked,Wise,Strong,Faraway,Secret,Blind,lost,City,Crazy,Cave,Dungeon,Cottage,Sky,Garden,Forest,Mountain,Palace,Kitchen,Night,Road,River,Swamp,Tower,Village,Kingdom,Church,Home,Ruin,King,Beggar,Fairy,Prince,Brother,Sister,Giant,Princess,Child,Wolf,Page,Queen,Stepmother,Frog,Guard,Parent,Horse,Cook,Husband,Wife,Monster,Enemy,Old,Dragon,Thief,Orphan,Witch,GARB_AA,GARB_AE,GARB_AH,GARB_AO,GARB_AW,GARB_AY,GARB_B,GARB_CH,GARB_D,GARB_DH,GARB_EH,GARB_ER,GARB_EY,GARB_F,GARB_G,GARB_HH,GARB_IH,GARB_IY,GARB_JH,GARB_K,GARB_L,GARB_M,GARB_N,GARB_NG,GARB_OW,GARB_OY,GARB_P,GARB_R,GARB_S,GARB_SH,GARB_T,GARB_TH,GARB_UH,GARB_UW,GARB_V,GARB_W,GARB_Y,GARB_Z,GARB_ZH".toUpperCase();

      var keynounsArr = keynouns.split(",");
      var prob = -999999;

      // add the keywords to the grammar in the correct gramma object format
      var grammarKeywords = {numStates: 1, start: 0, end: 0, transitions: []};
      for (var i = 0; i < keynounsArr.length; i++) {
        if (keynounsArr[i].substr(0,4) === "GARB") {
          prob = -777777;
        }
        grammarKeywords.transitions.push({from: 0, to: 0, logp: prob, word: keynounsArr[i]});
      };

      // These will be initialized later
      var recognizer, recorder, callbackManager, audioContext, outputContainer;
      // Only when both recorder and recognizer do we have a ready application
      var recorderReady = recognizerReady = false;

      // A convenience function to post a message to the recognizer and associate
      // a callback to its response
      function postRecognizerJob(message, callback) {
        var msg = message || {};
        if (callbackManager) msg.callbackId = callbackManager.add(callback);
        if (recognizer) recognizer.postMessage(msg);
      };

      // This function initializes an instance of the recorder
      // it posts a message right away and calls onReady when it
      // is ready so that onmessage can be properly set
      function spawnWorker(workerURL, onReady) {
          recognizer = new Worker(workerURL);
          recognizer.onmessage = function(event) {
            onReady(recognizer);
          };
          recognizer.postMessage('');
      };

      // To display the hypothesis sent by the recognizer
      function updateCount(count) {
        console.log("Got count " + count);
        if (outputContainer) outputContainer.innerHTML = count;
      };

      // This updates the UI when the app might get ready
      // Only when both recorder and recognizer are ready do we enable the buttons
      function updateUI() {
        if (recorderReady && recognizerReady) startBtn.disabled = stopBtn.disabled = false;
      };

      // This is just a logging window where we display the status
      function updateStatus(newStatus) {
        document.getElementById('current-status').innerHTML += "<br/>" + newStatus;
      };

      // A not-so-great recording indicator
      function displayRecording(display) {
        if (display) document.getElementById('recording-indicator').innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
        else document.getElementById('recording-indicator').innerHTML = "";
      };

      // Callback function once the user authorises access to the microphone
      // in it, we instanciate the recorder
      function startUserMedia(stream) {
        var input = audioContext.createMediaStreamSource(stream);
        // Firefox hack https://support.mozilla.org/en-US/questions/984179
        window.firefox_audio_hack = input; 
        var audioRecorderConfig = {errorCallback: function(x) {updateStatus("Error from recorder: " + x);}};
        recorder = new AudioRecorder(input, audioRecorderConfig);
        // If a recognizer is ready, we pass it to the recorder
        if (recognizer) recorder.consumers = [recognizer];
        recorderReady = true;
        updateUI();
        updateStatus("Audio recorder ready!");
      };

      // This starts recording. We first need to get the id of the keyword search to use
      var startRecording = function() {
        //var id = document.getElementById('keyword').value;
        if (recorder && recorder.start()) displayRecording(true);
      };

      // Stops recording
      var stopRecording = function() {
        recorder && recorder.stop();
        displayRecording(false);
      };

      // Called once the recognizer is ready
      // We then add the grammars to the input select tag and update the UI
      var recognizerReady = function() {
           recognizerReady = true;
           updateUI();
           updateStatus("Recogniser ready!");
      };

      // load our grammar object into pocketsphinx
      var feedGrammar = function(grammarKeywords, index, id) {
        postRecognizerJob({command: 'addGrammar', data: grammarKeywords});
        recognizerReady();
      };

      // To display the hypothesis sent by the recognizer on the webpage
      function updateHyp(hyp) {
        if (outputContainer) outputContainer.innerHTML = hyp.toLowerCase();
      };

      // get a gif of the last word spoken
      function getGif(lastword) {
      
      // placeholder when no internet
      // var gif = "img/jack.jpg";
      // $('#container-element').append("<img class='giphy' src='" + gif + "'/>");

      $.getJSON("http://api.giphy.com/v1/gifs/search?q="+lastword+"&api_key=<KEY>", function(data) {
        var ran = Math.floor(Math.random(0,data.data.length-1));
        //var giftype = "fixed_width_downsampled";
        var giftype = "fixed_height_still";
        var gif = data.data[ran].images[giftype].url;
        var img = $('body').append("<img class='giphy' src='" + gif + "'/>");
     });
    }

      // This adds words (from wordList.js) to the recognizer. When it calls back, we add grammars
      var feedWords = function(words) {
           postRecognizerJob({command: 'addWords', data: words},
                        function() {feedGrammar(grammarKeywords, 0);});
      };

      // This initializes the recognizer. When it calls back, we add words
      var initRecognizer = function() {
          // You can pass parameters to the recognizer, such as : {command: 'initialize', data: [["-hmm", "my_model"], ["-fwdflat", "no"]]}
          postRecognizerJob({command: 'initialize', data: [["-remove_noise", "yes"]]},
                            function() {
                                        if (recorder) recorder.consumers = [recognizer];
                                        // feed words from the wordList.js file
                                        feedWords(wordList);});
      };

      // When the page is loaded, we spawn a new recognizer worker and call getUserMedia to
      // request access to the microphone
      window.onload = function() {
        outputContainer = document.getElementById("output");
        updateStatus("Recogniser init, asking for microphone access...");
        callbackManager = new CallbackManager();
        spawnWorker("js/recognizer.js", function(worker) {
          // This is the onmessage function, once the worker is fully loaded
          worker.onmessage = function(e) {

            // This is the case when we have a callback id to be called
            if (e.data.hasOwnProperty('id')) {
              var clb = callbackManager.get(e.data['id']);
              var data = {};
              if ( e.data.hasOwnProperty('data')) data = e.data.data;
              if(clb) clb(data);
            }
            // This is a case when the recognizer has a new hypothesis
            if (e.data.hasOwnProperty('hyp')) {
              var newHyp = e.data.hyp;
              // put all words in hypothesis string into an array for use
              var lastsp = newHyp.split(' ');
              var lastspLen = lastsp.length;

              // this gets the last word spoken in the concatenated hypothesis
              var lastword = lastsp.pop();

              // was the last word garbage of background noise?
              var garbage = (lastword.substr(0,4) === "GARB") ? true : false;

              // if the word is not a duplicate, blank, or garbage, then grab a GIF!
              if (lastword !== prevWord && lastword !== '' && !garbage) {
                getGif(lastword);
              }
              
              if (e.data.hasOwnProperty('final') &&  e.data.final) newHyp = "Final: " + newHyp;

              // if there's background noise, suggest to the user that this is so.
              if (garbage) { lastword = "* random noise *" };

              // send the last word recognised to the UI
              updateHyp(lastword);

              prevWord = lastword;

            }
            // This is the case when we have an error
            if (e.data.hasOwnProperty('status') && (e.data.status == "error")) {
              updateStatus("Error in " + e.data.command + " with code " + e.data.code);
            }

          };
          // Once the worker is fully loaded, we can call the initialize function
          initRecognizer();
        });

        // The following is to initialize Web Audio
        try {
          window.AudioContext = window.AudioContext || window.webkitAudioContext;
          navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
          window.URL = window.URL || window.webkitURL;
          audioContext = new AudioContext();
        } catch (e) {
          updateStatus("Error initializing Web Audio browser");
        }

        if (navigator.getUserMedia) {
          navigator.getUserMedia({audio: true}, startUserMedia, function(e) { updateStatus("No live audio input in this browser"); });
        } else {
          updateStatus("No web audio support in this browser");
        }

        // Wiring JavaScript to the UI
        var startBtn = document.getElementById('startBtn');
        var stopBtn = document.getElementById('stopBtn');
        startBtn.disabled = true;
        stopBtn.disabled = true;
        startBtn.onclick = startRecording;
        stopBtn.onclick = stopRecording;
      };
        
    </script>

    <script src="js/audioRecorder.js"></script>
    <script src="js/callbackManager.js"></script>
    
  </body>
</html>

{'fraction_non_alphanumeric': 0.12257817663223068, 'fraction_numerical': 0.013356986329959302, 'mean_word_length': 2.225987432675045, 'pattern_counts': {'":': 0, '<': 105, '<?xml version=': 0, '>': 103, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7512497', 'n_tokens_mistral': 9035, 'n_tokens_neox': 8527, 'n_words': 2121}