Index: main/trunk/greenstone3/web/interfaces/default/js/utility_scripts.js
===================================================================
--- main/trunk/greenstone3/web/interfaces/default/js/utility_scripts.js	(revision 36145)
+++ main/trunk/greenstone3/web/interfaces/default/js/utility_scripts.js	(revision 36163)
@@ -167,4 +167,6 @@
 }
 
+// TK Label Scripts
+
 var tkMetadataSetStatus = "needs-to-be-loaded";
 var tkMetadataElements = null;
@@ -192,6 +194,11 @@
                         let popupText = document.createElement("span");
                         let heading = "<h1>Traditional Knowledge Label:<br><h2>" + name + "</h2></h1>";
-                        let moreInformation = "<br> For more information about TK Labels, <a href='https://localcontexts.org/labels/traditional-knowledge-labels/' target='_blank'>click here.</a>";
+                        let moreInformation = "<br> For more information about TK Labels, ";
+                        let link = document.createElement("a");
+                        link.innerHTML = "click here.";
+                        link.href = "https://localcontexts.org/labels/traditional-knowledge-labels/";
+                        link.target = "_blank";
                         popupText.innerHTML = heading + comment + moreInformation;
+                        popupText.appendChild(link);
                         let closeButton = document.createElement("span");
                         closeButton.innerHTML = "&#215;";
@@ -253,3 +260,261 @@
 };
 
-
+// Audio Scripts for Enriched Playback
+
+function loadAudio(audio, sectionData) {
+   var speakerObjects = [];
+   var uniqueSpeakers;
+   var prevColour;
+   // const inputFile = "output.csv";
+   const inputFile = sectionData;
+   // audio = "audio/akl_mi_pk_0002.wav";
+   var itemType;
+
+   var wavesurfer = WaveSurfer.create({
+      container: document.querySelector('#waveform'),
+      backend: 'MediaElement',
+      backgroundColor: 'rgb(54, 73, 78)',
+      waveColor: 'white',
+      progressColor: '#F8C537',
+      barWidth: 2,
+      barHeight: 1,
+      barGap: 2,
+      barRadius: 1,
+      cursorColor: 'black',
+      plugins: [
+         WaveSurfer.regions.create(),
+         WaveSurfer.timeline.create({
+            container: "#wave-timeline",
+            primaryColor: "white",
+            secondaryColor: "white",
+            primaryFontColor: "white",
+            secondaryFontColor: "white"
+         }),
+      ],
+   });
+
+   // wavesurfer.load('https://wavesurfer-js.org/example/elan/transcripts/001z.mp3');
+   wavesurfer.load(audio);
+
+   wavesurfer.on('region-click', function(region, e) {
+      e.stopPropagation();
+      regionMouseEvent(region, true);
+      wavesurfer.play(region.start);
+      // region.play();
+   });
+
+   wavesurfer.on('region-mouseenter', function(region) { regionMouseEvent(region, true); });
+   wavesurfer.on('region-mouseleave', function(region) { 
+      if (wavesurfer.getCurrentTime() <= region.end && wavesurfer.getCurrentTime() >= region.start) {
+      //   console.log("");
+      } else {
+         regionMouseEvent(region, false); 
+      }
+   });
+   wavesurfer.on('region-in', function(region) { regionMouseEvent(region, true); });
+   wavesurfer.on('region-out', function(region) { regionMouseEvent(region, false); });
+
+   function regionMouseEvent(region, highlight) {
+      var colour;
+      if (highlight) {
+         colour = "rgb(101, 116, 116)";
+         regionEnter(region);
+      } else {
+         colour = "";
+         regionLeave(region);
+      }
+      var regionIndex = region.id.replace("region","");
+      var corrItem = corrItem = document.getElementById(itemType + regionIndex);
+      corrItem.style.backgroundColor = colour;
+   }
+
+   var chapterButton = document.getElementById("chapterButton");
+   var backButton = document.getElementById("backButton");
+   var playPauseButton = document.getElementById("playPauseButton");
+   var forwardButton = document.getElementById("forwardButton");
+   var muteButton = document.getElementById("muteButton");
+   chapterButton.addEventListener("click", function() { toggleChapters(); });
+   backButton.addEventListener("click", function() { wavesurfer.skipBackward(); });
+   playPauseButton.addEventListener("click", function() { wavesurfer.playPause() });
+   forwardButton.addEventListener("click", function() { wavesurfer.skipForward(); });
+   muteButton.addEventListener("click", function() { wavesurfer.toggleMute(); });
+
+   var chapters = document.getElementById("chapters");
+   chapters.style.height = "0px";
+
+   if (inputFile.endsWith("csv")) {
+      itemType = "chapter";
+      loadCSVFile(inputFile, ["speaker", "start", "end"]);
+   } else if (inputFile.endsWith("json")) {
+      itemType = "word";
+      loadJSONFile(inputFile);
+   } else {
+      console.log("Filetype of " + inputFile + " not supported.")
+   }
+
+   var interface_bootstrap_images = "interfaces/" + gs.xsltParams.interface_name + "/images/bootstrap/";
+
+   wavesurfer.on("play", function() { playPauseButton.src = interface_bootstrap_images + "pause.svg"; });
+   wavesurfer.on("pause", function() { playPauseButton.src = interface_bootstrap_images + "play.svg"; });
+   wavesurfer.on("mute", function(mute) { 
+      if (mute) muteButton.src = interface_bootstrap_images + "mute.svg"; 
+      else muteButton.src = interface_bootstrap_images + "unmute.svg"; 
+   });
+
+   document.querySelector('#slider').oninput = function () {
+      wavesurfer.zoom(Number(this.value));
+   };
+
+   var toggleChapters = function() {
+      if (chapters.style.height == "0px") {
+         chapters.style.height = "500px";
+      } else {
+         chapters.style.height = "0px";
+      }
+   }
+
+   function loadCSVFile(filename, manualHeader) { // based around: https://stackoverflow.com/questions/7431268/how-to-read-data-from-csv-file-using-javascript
+      $.ajax({
+         type: "GET",
+         url: filename,
+         dataType: "text",
+      }).then(function(data) {
+         
+         var dataLines = data.split(/\r\n|\n/);
+         var headers;
+         var startIndex;
+         uniqueSpeakers = [];
+         speakerObjects = [];
+
+         if (manualHeader) {
+            headers = manualHeader;
+            startIndex = 0;
+         } else {
+            headers = dataLines[0].split(',');
+            startIndex = 1;
+         }
+
+         for (var i = startIndex; i < dataLines.length; i++) {
+            var data = dataLines[i].split(',');
+            if (data.length == headers.length) {
+                  var item = {};
+                  for (var j = 0; j < headers.length; j++) {
+                     item[headers[j]] = data[j];
+                     if (j == 0) {
+                        if (!uniqueSpeakers.includes(data[j])) {
+                              uniqueSpeakers.push(data[j]);
+                        }
+                     }
+                  }
+                  speakerObjects.push(item);
+            }
+         }
+         populateChapters(speakerObjects);
+      });
+   }
+
+   function populateChapters(data) {
+      // TODO: colorbrewer info
+      colourbrewerset = colorbrewer.Set2[uniqueSpeakers.length];
+      for (var i = 0; i < data.length; i++) {
+         var chapter = document.createElement("div");
+         chapter.id = "chapter" + i;
+         chapter.classList.add("chapter");
+         chapter.innerHTML = data[i].speaker + "<span class='speakerTime' id='" + "chapter" + i + "'>" + data[i].start + " - " + data[i].end + "s</span>";
+         chapter.addEventListener("click", e => { chapterClicked(e.target.id) });
+         chapter.addEventListener("mouseover", e => { chapterEnter(e.target.id) });
+         chapter.addEventListener("mouseleave", e => { chapterLeave(e.target.id) });
+         chapters.appendChild(chapter);
+         wavesurfer.addRegion({
+            id: "region" + i,
+            start: data[i].start,
+            end: data[i].end,
+            drag: false,
+            resize: false,
+            // color: "rgba(255, 255, 255, 0.2)",
+            color: colourbrewerset[uniqueSpeakers.indexOf(data[i].speaker)] + "66",
+         });
+      }
+   }
+
+   function loadJSONFile(filename) {
+      $.ajax({
+         type: "GET",
+         url: filename,
+         dataType: "text",
+      }).then(function(data){populateWords(JSON.parse(data))});
+   }
+
+   function populateWords(data) {
+      var transcription = data.transcription;
+      var words = data.words;
+      var wordContainer = document.createElement("div");
+      wordContainer.id = "word-container";
+      for (var i = 0; i < words.length; i++) {
+         var word = document.createElement("span");
+         word.id = "word" + i;
+         word.classList.add("word");
+         word.innerHTML = transcription.split(" ")[i];
+         word.addEventListener("click", e => { wordClicked(data, e.target.id) });
+         word.addEventListener("mouseover", e => { chapterEnter(e.target.id) });
+         word.addEventListener("mouseleave", e => { chapterLeave(e.target.id) });
+         wordContainer.appendChild(word);
+         wavesurfer.addRegion({
+            id: "region" + i,
+            start: words[i].startTime,
+            end: words[i].endTime,
+            drag: false,
+            resize: false,
+            color: "rgba(255, 255, 255, 0.1)",
+         });
+      }
+      chapters.appendChild(wordContainer);
+   }
+
+   var chapterClicked = function(id) {
+      var index = id.replace("chapter", "");
+      var start = speakerObjects[index].start;
+      var end = speakerObjects[index].end;
+      // wavesurfer.play(start, end);
+      wavesurfer.play(start);
+   }
+
+   function wordClicked(data, id) {
+      var index = id.replace("word", "");
+      var start = data.words[index].startTime;
+      var end = data.words[index].endTime;
+      // wavesurfer.play(start, end);
+      wavesurfer.play(start);
+   }
+
+   function chapterEnter(id) {
+      regionEnter(wavesurfer.regions.list["region" + id.replace(itemType, "")]);
+   }
+
+   function chapterLeave(id) {
+      regionLeave(wavesurfer.regions.list["region" + id.replace(itemType, "")]);
+   }
+
+   function regionEnter(region) {
+      setPrevColour(region);
+      region.update({ color: "rgba(255, 255, 255, 0.35)" });
+   }
+
+   function regionLeave(region) {
+      if (itemType == "chapter") {
+         region.update({ color: prevColour });
+         // chapters.innerHTML = "";
+         // wavesurfer.clearRegions();
+         // populateChapters(speakerObjects);
+
+      } else {
+         region.update({ color: "rgba(255, 255, 255, 0.1)" });
+      }
+   }
+
+   function setPrevColour(region) {
+      var regionIndex = region.id.replace("region","");
+      var corrItem = corrItem = document.getElementById(itemType + regionIndex);
+      prevColour = colourbrewerset[uniqueSpeakers.indexOf(corrItem.firstChild.data)] + "66";
+   }
+}
