@@ -168,6 +168,7 @@ a.anchor-link {
168
168
</div>
169
169
</div>
170
170
-->
171
+ <div class =" clock" style =" margin : 5px 5px 0px 0px ; float : right ;" ></div >
171
172
<div class =" slides" >
172
173
{% - endblock body_header -%}
173
174
@@ -181,7 +182,8 @@ require(
181
182
},
182
183
[
183
184
" {{ reveal_url_prefix }}/dist/reveal.js" ,
184
- " {{ reveal_url_prefix }}/plugin/notes/notes.js"
185
+ " {{ reveal_url_prefix }}/plugin/notes/notes.js" ,
186
+ " https://cdn.jsdelivr.net/npm/luxon@3.2.1/build/global/luxon.min.js"
185
187
],
186
188
187
189
function (Reveal , RevealNotes ){
@@ -205,6 +207,8 @@ require(
205
207
}
206
208
});
207
209
210
+ const isVisible = (display ) => display === ' none' ? ' block' : ' none' ;
211
+
208
212
// jump to slide prompt
209
213
Reveal .addKeyBinding (
210
214
{ keyCode: 71 , key: " G" , description: " Go to slide" },
@@ -226,6 +230,120 @@ require(
226
230
}
227
231
);
228
232
233
+ // for working with dates/times
234
+ const { DateTime , Duration } = luxon;
235
+
236
+ // add exercise timer
237
+ let updateExerciseTimerInterval;
238
+ Reveal .addKeyBinding (
239
+ { keyCode: 84 , key: " T" , description: " Start the exercise timer" },
240
+ () => {
241
+ let exerciseTime = prompt (" Timer duration in minutes (leave empty to cancel)" );
242
+ const cleanup = () => {
243
+ clearInterval (updateExerciseTimerInterval);
244
+ $ (' .exercise-timer' ).remove ();
245
+ }
246
+ if (exerciseTime !== undefined && exerciseTime !== " " && exerciseTime !== null ){
247
+ if (! isNaN (exerciseTime) && exerciseTime >= 0.1 ) {
248
+ exerciseTime = Duration .fromMillis (exerciseTime * 60 * 1000 );
249
+ const countDownTime = DateTime .now ().plus (exerciseTime);
250
+
251
+ const getTimeText = (elapsedTime ) => ` ⏱ ${ elapsedTime .toFormat (' mm:ss' )} ` ;
252
+
253
+ if ($ (' .exercise-timer' ).length === 0 ) { // add to DOM if not already there
254
+ $ (' .reveal' ).append (
255
+ ` <aside class="exercise-timer" style="display: block; position: absolute; `
256
+ + ` top: 10px; left: 15px; right: auto; bottom: auto;`
257
+ + ` font-size: 50px;"></aside>`
258
+ );
259
+ }
260
+
261
+ // in case the key is pressed while a timer is already running, clear the old one
262
+ if (! isNaN (updateExerciseTimerInterval)) clearInterval (updateExerciseTimerInterval);
263
+
264
+ // write the starting time minus the 1 second delay before the update
265
+ const updateFrequency = 1000 ;
266
+ $ (' .exercise-timer' ).text (getTimeText (exerciseTime .minus ({ milliseconds: updateFrequency })));
267
+
268
+ // update the remaining time each second
269
+ const updateTimer = () => {
270
+ const now = DateTime .now ();
271
+ const elapsedTime = countDownTime .diff (now);
272
+ const secondsRemaining = elapsedTime .as (' seconds' )
273
+
274
+ if (secondsRemaining >= 1 ) $ (' .exercise-timer' ).text (getTimeText (elapsedTime));
275
+ else if (secondsRemaining >= 0 ) {
276
+ $ (' .exercise-timer' ).text (getTimeText (Duration .fromMillis (0 )));
277
+ const audio = new Audio (' ../../media/time_is_up.m4a' ); // TODO: switch to URL of hosted version
278
+ audio .play ();
279
+ }
280
+ else if (secondsRemaining >= - 5 ) $ (' .exercise-timer' ).text (" TIME'S UP" ).css (' color' , ' red' );
281
+ else cleanup ();
282
+ }
283
+ updateExerciseTimerInterval = setInterval (updateTimer, updateFrequency);
284
+ }
285
+ }
286
+ else cleanup ();
287
+ }
288
+ );
289
+
290
+ // add clock
291
+ let updateClockInterval;
292
+ Reveal .addKeyBinding (
293
+ { keyCode: 67 , key: " C" , description: " Toggle the clock" },
294
+ () => {
295
+ if (! isNaN (updateClockInterval)) {
296
+ clearInterval (updateClockInterval);
297
+ $ (' .clock' ).text (' ' );
298
+ updateClockInterval = ' cleared' ;
299
+ }
300
+ else {
301
+ const updateTime = () => $ (' .clock' ).text (DateTime .now ().toFormat (' hh:mm a' ));
302
+ updateTime ();
303
+ updateClockInterval = setInterval (updateTime, 1000 );
304
+ }
305
+ }
306
+ );
307
+
308
+ // show clock in red when 5 minutes from desired time
309
+ let updateFMWInterval;
310
+ Reveal .addKeyBinding (
311
+ { keyCode: 90 , key: " Z" , description: " Set 5-minute warning" },
312
+ () => {
313
+ const input = prompt (" What is the ending time? (leave empty to clear)" );
314
+ const cleanup = () => {
315
+ clearInterval (updateFMWInterval);
316
+ $ (' .clock' ).text (' ' ).css (' color' , ' black' );
317
+ updateFMWInterval = ' cleared' ;
318
+ }
319
+ if (input !== undefined && input !== " " && input !== null && input .includes (' :' )){
320
+
321
+ if (! isNaN (updateFMWInterval)) cleanup ();
322
+
323
+ const [time , ampm ] = input .split (' ' );
324
+ let [endHour, endMinute] = time .split (' :' ).map ((x ) => parseInt (x));
325
+ const militaryTime = ampm === undefined ;
326
+ let shiftHours = militaryTime ? 0 : (ampm .toLowerCase () === ' pm' ? 12 : 0 );
327
+
328
+ const endAt = DateTime .now ().startOf (' minute' ).set ({
329
+ hour: endHour + shiftHours,
330
+ minute: endMinute
331
+ });
332
+ const warnAt = endAt .minus ({ minutes: 5 });
333
+
334
+ const checkTime = () => {
335
+ const now = DateTime .now ();
336
+ if (now >= endAt) cleanup ();
337
+ else if (now >= warnAt) {
338
+ $ (' .clock' ).text (now .toFormat (' hh:mm a' )).css (' color' , ' red' );
339
+ }
340
+ }
341
+ updateFMWInterval = setInterval (checkTime, 1000 );
342
+ }
343
+ else cleanup ();
344
+ }
345
+ );
346
+
229
347
var update = function (event ){
230
348
if (MathJax .Hub .getAllJax (Reveal .getCurrentSlide ())){
231
349
MathJax .Hub .Rerender (Reveal .getCurrentSlide ());
0 commit comments