Right, popup now appears where it should and has the right content.
Tutor sound is playing correctly. Student sound not yet wired up.
This commit is contained in:
parent
4a3bb586a4
commit
4f3b14339f
BIN
resources/public/img/github-logo-transparent.png
Normal file
BIN
resources/public/img/github-logo-transparent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
File diff suppressed because it is too large
Load diff
82
resources/public/scripts/muharni.js
Normal file
82
resources/public/scripts/muharni.js
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
* muharni.js
|
||||||
|
*
|
||||||
|
* Custom JavaScript functions, some dependent on JQuery, used in the `muharni`
|
||||||
|
* page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array for client side sound recordings made by the student. We don't
|
||||||
|
* strictly need one for each short and long character, but it will give a
|
||||||
|
* better experience for people who really get into it.
|
||||||
|
*
|
||||||
|
* There are two tables each of 39 rows and 12 columns, but we're going to
|
||||||
|
* pack both tables into the same array. So the long sounds will be rows 1
|
||||||
|
* to 39 inclusive, and the short from 41 to 79 inclusive.
|
||||||
|
*/
|
||||||
|
const studentSounds = Array(80).fill(0).map(x => Array(13).fill(null));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function recordStudentSound() {
|
||||||
|
if (currentCell) {
|
||||||
|
$('#record-student').css('background-color', 'green');
|
||||||
|
navigator.mediaDevices.getUserMedia({ audio: true })
|
||||||
|
.then(stream => {
|
||||||
|
const mediaRecorder = new MediaRecorder(stream);
|
||||||
|
mediaRecorder.start();
|
||||||
|
|
||||||
|
const audioChunks = [];
|
||||||
|
|
||||||
|
mediaRecorder.addEventListener("dataavailable", event => {
|
||||||
|
audioChunks.push(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
mediaRecorder.stop();
|
||||||
|
if (audioChunks.length > 0) {
|
||||||
|
studentSounds[currentCell.row][currentCell.col] = new Blob(audioChunks);
|
||||||
|
$('#play-student').prop('disabled', false);
|
||||||
|
}
|
||||||
|
$('#record-student').css('background-color', 'red');
|
||||||
|
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a student sound has been recorded for the current cell, play it.
|
||||||
|
*/
|
||||||
|
function playStudentSound() {
|
||||||
|
if (currentCell) {
|
||||||
|
if (studentSounds[currentCell.row][currentCell.col] != null) {
|
||||||
|
new Audio(URL.createObjectURL(studentSounds[currentCell.row][currentCell.col])).play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$(".entry").on("click", function(e) {
|
||||||
|
let cellId = e.currentTarget ? e.currentTarget.id : null;
|
||||||
|
|
||||||
|
if (cellId)
|
||||||
|
{
|
||||||
|
$("#popup").css({
|
||||||
|
'left': e.pageX,
|
||||||
|
'top': e.pageY
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#character").text(e.currentTarget.innerText);
|
||||||
|
|
||||||
|
$("#play-tutor").off("click"); /* trying to remove any previous click handler */
|
||||||
|
$("#play-tutor").on("click", function(e){
|
||||||
|
let audioUrl = "audio/" + cellId.slice(1) + ".mp3";
|
||||||
|
new Audio( audioUrl).play();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#popup").show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
|
@ -50,4 +50,5 @@ th {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: none;
|
display: none;
|
||||||
background-color: whitesmoke;
|
background-color: whitesmoke;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
|
@ -83,25 +83,28 @@
|
||||||
"Emit a table cell describing one entry from entries with either the
|
"Emit a table cell describing one entry from entries with either the
|
||||||
long or short audio clip available on click. "
|
long or short audio clip available on click. "
|
||||||
[^Integer row ^Integer col ^Boolean long?]
|
[^Integer row ^Integer col ^Boolean long?]
|
||||||
(let [audio (format
|
(let [r (inc row)
|
||||||
"audio/%02d%s.mp3"
|
c ((columns col) (if long? :upper-latin :lower-latin))
|
||||||
(inc row)
|
audio (format "audio/%02d%s.mp3" r c)
|
||||||
((columns col) (if long? :upper-latin :lower-latin)))
|
|
||||||
char ((entries row) col)]
|
char ((entries row) col)]
|
||||||
(vector :td {:class "entry"
|
(vector :td {:class "entry"
|
||||||
|
:id (format "%s%02d%s" (if long? "l" "s") r c)
|
||||||
:onclick (format
|
;; :onclick (format
|
||||||
"showPopup( '%s', '%s', '%s', '%s')"
|
;; "showPopup( '%s', '%s', '%s', '%s')"
|
||||||
(if long? row (+ row 40))
|
;; (if long? row (+ row 40))
|
||||||
col
|
;; col
|
||||||
char
|
;; char
|
||||||
audio)}
|
;; audio)
|
||||||
|
}
|
||||||
;; (audio row col long?)
|
;; (audio row col long?)
|
||||||
char)))
|
char)))
|
||||||
|
|
||||||
;; (entry-cell 3 4 true)
|
;; (entry-cell 3 4 true)
|
||||||
|
|
||||||
(defn all-entries-cell
|
(defn all-entries-cell
|
||||||
|
"Return a table cell which plays the sound for all entries in the specified
|
||||||
|
`row`, concatenated from left to right if `ltr?` is true else right to left,
|
||||||
|
and playing long recordings if `long?` is true else short recordings."
|
||||||
[^Integer row ^Boolean ltr? ^Boolean long?]
|
[^Integer row ^Boolean ltr? ^Boolean long?]
|
||||||
[:td {:class "play-row"
|
[:td {:class "play-row"
|
||||||
:onclick (format "new Audio('audio/%s%s%02d_MP3WRAP.mp3').play();"
|
:onclick (format "new Audio('audio/%s%s%02d_MP3WRAP.mp3').play();"
|
||||||
|
@ -112,6 +115,8 @@
|
||||||
|
|
||||||
|
|
||||||
(defn entries-row
|
(defn entries-row
|
||||||
|
"Return a table row for the specified `row` number for long recordings if
|
||||||
|
`long?` is true else short recordings."
|
||||||
[^Integer row ^Boolean long?]
|
[^Integer row ^Boolean long?]
|
||||||
(apply vector
|
(apply vector
|
||||||
(concat [:tr]
|
(concat [:tr]
|
||||||
|
@ -122,9 +127,10 @@
|
||||||
;; (entries-row 3 true)
|
;; (entries-row 3 true)
|
||||||
|
|
||||||
(defn col-header-cell
|
(defn col-header-cell
|
||||||
[^Integer col]
|
"Return a header cell for the indicated `column`."
|
||||||
|
[^Integer column]
|
||||||
(vector :th
|
(vector :th
|
||||||
(:name (columns col))))
|
(:name (columns column))))
|
||||||
|
|
||||||
(defn col-headers-row
|
(defn col-headers-row
|
||||||
[]
|
[]
|
||||||
|
@ -136,6 +142,9 @@
|
||||||
;; (col-headers-row)
|
;; (col-headers-row)
|
||||||
|
|
||||||
(defn play-column-row
|
(defn play-column-row
|
||||||
|
"Return a table row of cells which play concatenated sounds for each column,
|
||||||
|
concatenating from top to bottom is `ttb?` is true, else bottom to top, and
|
||||||
|
playing long recordings if `long?` is true else short recordings."
|
||||||
[^Boolean ttb? ^Boolean long?]
|
[^Boolean ttb? ^Boolean long?]
|
||||||
(apply vector
|
(apply vector
|
||||||
(concat [:tr]
|
(concat [:tr]
|
||||||
|
@ -147,8 +156,7 @@
|
||||||
"new Audio('audio/%s%s%s_MP3WRAP.mp3').play();"
|
"new Audio('audio/%s%s%s_MP3WRAP.mp3').play();"
|
||||||
(if ttb? "ttb" "btt")
|
(if ttb? "ttb" "btt")
|
||||||
(if long? "long" "short")
|
(if long? "long" "short")
|
||||||
((columns %) (if long? :upper-latin :lower-latin)))
|
((columns %) (if long? :upper-latin :lower-latin)))}
|
||||||
}
|
|
||||||
(str "Play " (if ttb? "down" "up")))
|
(str "Play " (if ttb? "down" "up")))
|
||||||
(range (count columns)))
|
(range (count columns)))
|
||||||
[[:td]])))
|
[[:td]])))
|
||||||
|
@ -156,6 +164,8 @@
|
||||||
;; (play-column-row true true)
|
;; (play-column-row true true)
|
||||||
|
|
||||||
(defn table
|
(defn table
|
||||||
|
"Lay out a muharni table, playing long recordings if `long?` is true else
|
||||||
|
short recordings."
|
||||||
[^Boolean long?]
|
[^Boolean long?]
|
||||||
(apply
|
(apply
|
||||||
vector
|
vector
|
||||||
|
@ -171,6 +181,7 @@
|
||||||
;; (table true)
|
;; (table true)
|
||||||
|
|
||||||
(defn page
|
(defn page
|
||||||
|
"Construct the complete muharni tables page."
|
||||||
[title]
|
[title]
|
||||||
[:html
|
[:html
|
||||||
[:head
|
[:head
|
||||||
|
@ -190,12 +201,12 @@
|
||||||
[:table {:id "controls" :summary "Controls for audio playback and recording"}
|
[:table {:id "controls" :summary "Controls for audio playback and recording"}
|
||||||
[:tr
|
[:tr
|
||||||
[:th "Tutor"]
|
[:th "Tutor"]
|
||||||
[:td {:id "play-tutor"} [:button {:onclick "playTutorSound();"}
|
[:td [:button {:id "play-tutor"}
|
||||||
"►"]]]
|
"►"]]]
|
||||||
[:tr
|
[:tr
|
||||||
[:th "You"]
|
[:th "You"]
|
||||||
[:td {:id "play-student"} [:button {:onclick "playStudentSound();"} "►"]]
|
[:td [:button {:id "play-student"} "►"]]
|
||||||
[:td {:id "record-stop"} [:button {:onclick "recordStudentSound();"} "⏺"]]]]]
|
[:td [:button {:id "record-stop"} "⏺"]]]]]
|
||||||
[:h1 (str title)]
|
[:h1 (str title)]
|
||||||
[:button {:onclick "var l = document.getElementById('long');
|
[:button {:onclick "var l = document.getElementById('long');
|
||||||
var s = document.getElementById('short');
|
var s = document.getElementById('short');
|
||||||
|
@ -223,9 +234,7 @@
|
||||||
[:a {:href "https://github.com/simon-brooke/muharni"} "GitHub"]]]]])
|
[:a {:href "https://github.com/simon-brooke/muharni"} "GitHub"]]]]])
|
||||||
|
|
||||||
(defn tidy-page
|
(defn tidy-page
|
||||||
"Reads HTML as a string, emits cleaned-up HTML as a string. This is not yet
|
"Reads HTML from `page` as a string, returns cleaned-up HTML as a string."
|
||||||
working with HTML as produced by Hiccup, since Hiccup is currently
|
|
||||||
entitifying single quotes within (JavaScript) strings."
|
|
||||||
[^String page]
|
[^String page]
|
||||||
(let [tidy (Tidy.)
|
(let [tidy (Tidy.)
|
||||||
props (doto (Properties.)
|
props (doto (Properties.)
|
||||||
|
@ -239,14 +248,13 @@
|
||||||
;; (set! (. config indentContent) true)
|
;; (set! (. config indentContent) true)
|
||||||
;; (set! (. config smartIndent) true)
|
;; (set! (. config smartIndent) true)
|
||||||
(doto (.getConfiguration tidy) (.addProps props) (.adjust))
|
(doto (.getConfiguration tidy) (.addProps props) (.adjust))
|
||||||
(.parse tidy (input-stream (.getBytes page))
|
;; NOTE: Hiccup is currently entitifying single quotes within
|
||||||
|
;; (JavaScript) strings. This is NOT desirable behaviour!
|
||||||
|
(.parse tidy (input-stream (.getBytes (s/replace page #"\'" "'")))
|
||||||
output)
|
output)
|
||||||
(.toString output)))
|
(str "<!DOCTYPE html>\n" (.toString output))))
|
||||||
|
|
||||||
(spit "resources/public/index.html"
|
(spit "resources/public/index.html"
|
||||||
(tidy-page
|
(tidy-page
|
||||||
(s/replace
|
|
||||||
(str (html
|
(str (html
|
||||||
;;{:escape-strings? false}
|
(page "Muharni table")))))
|
||||||
(page "Muharni table")))
|
|
||||||
#"\'" "'")))
|
|
||||||
|
|
Loading…
Reference in a new issue