source: gs3-installations/mars/trunk/sites/mars/collect/amc-essentia/js/av_document.js@ 37256

Last change on this file since 37256 was 37256, checked in by davidb, 16 months ago

Development of 'personal bias' in localStorage; Working if user starts and stops recording, but bug to track down if they let the recording play all the way to the end and trigger the finish() callback

File size: 35.1 KB
Line 
1var AMC_SONG_DURATION = 30;
2
3var currentPosIsZero = true;
4var currentPosRunup = 0;
5
6
7function AVEnsurePaused()
8{
9 if (!wavesurfer.backend.isPaused()) {
10 $('#ws-pause-icon').hide();
11 $('#ws-play-icon' ).show();
12 $('#ws-playpause-label').html("Play");
13 }
14
15 wavesurfer.pause();
16}
17
18function AVPlay()
19{
20 // Restore play/pause toggle button back to 'ready to play' i.e., showing pause button
21 $('#ws-play-icon' ).hide();
22 $('#ws-pause-icon').show();
23 $('#ws-playpause-label').html("Pause");
24
25 if (currentPosRunup>0) {
26 var skip_amount = -1 * currentPosRunup;
27 wavesurfer.skip(skip_amount);
28 currentPosRunup = 0;
29 }
30
31 wavesurfer.play();
32
33}
34
35function AVPlayPause()
36{
37 if (wavesurfer.backend.isPaused()) {
38 AVPlay();
39 }
40 else {
41 AVEnsurePaused();
42 }
43
44 //return wavesurfer.backend.isPaused() ? wavesurfer.play() : wavesurfer.pause();
45}
46
47function AVSeekTo(progressPos)
48{
49 wavesurfer.seekTo(progressPos);
50
51 if (progressPos == 0) {
52 recommendFromStart();
53 }
54}
55
56
57
58/*
59 function playFromOffset(id,frameOffset)
60 {
61 var msecOffset = 250 * frameOffset;
62 idElem = document.getElementById(id);
63 pagePlayer.handleClick({target:idElem}); // fake a click
64 soundManager.stopAll();
65 // soundManager.setPosition(id,5000);
66 soundManager.play(id,{position: msecOffset});
67 }
68 */
69
70
71function displayClampedCurrentTimeAndAV()
72{
73 // Note, the Arousal and Valence feature window values used is:
74 // 6 secs + 50% overlap
75 // So the Weka computed AV values are spaced out:
76 // 6s, 9s, 12s, ...
77
78 // clamp to ensure >= 6.0 secs
79
80 var current_time = Math.max(6.0,wavesurfer.getCurrentTime());
81 var current_time_1dp = current_time.toFixed(1);
82
83 var arousal_val = parseFloat($('#arousal-val').text());
84 var valence_val = parseFloat($('#valence-val').text());
85
86 var arousal_val_str = (arousal_val>0) ? "+"+arousal_val : ""+arousal_val;
87 var valence_val_str = (valence_val>0) ? "+"+valence_val : ""+valence_val;
88
89 $('#makeRecommendationFrom-AV').html(`(@${current_time_1dp} secs: arousal = ${arousal_val_str}, valence = ${valence_val_str})`);
90
91 return current_time;
92}
93
94function preSubmit(form)
95{
96 // Note, the Arousal and Valence feature window values used is:
97 // 6 secs + 50% overlap
98 // So the Weka computed AV values are spaced out:
99 // 6s, 9s, 12s, ...
100
101 // clamp to ensure >= 6.0 secs
102 //var current_time = Math.max(6.0,wavesurfer.getCurrentTime());
103 //var current_time_1dp = current_time.toFixed(1);
104
105 var clamped_current_time = displayClampedCurrentTimeAndAV();
106
107 AVEnsurePaused();
108
109 weka_segment = Math.round(clamped_current_time/3) * 3;
110 form.elements["s1.offset"].value = weka_segment;
111
112 var arousal_val = parseFloat($('#arousal-val').text());
113 var valence_val = parseFloat($('#valence-val').text());
114
115 //$('#makeRecommendationFrom-AV').html(`(@${current_time_1dp} secs: arousal=${arousal_val}, valence=${valence_val})`);
116
117 return submitAVRecommendation(form, arousal_val,valence_val, clamped_current_time);
118}
119
120function AVPlotRecommendations()
121{
122 var $av_recommendations = $('.AVRecommendation');
123
124 var min_arousal_val = +1.0;
125 var max_arousal_val = -1.0;
126
127 var min_valence_val = +1.0;
128 var max_valence_val = -1.0;
129
130 $av_recommendations.each(function(index_unused) {
131 var arousal_val = $(this).data("arousalval");
132 var valence_val = $(this).data("valenceval");
133
134 if (arousal_val < min_arousal_val) {
135 min_arousal_val = arousal_val;
136 }
137 if (arousal_val > max_arousal_val) {
138 max_arousal_val = arousal_val;
139 }
140
141
142 if (valence_val < min_valence_val) {
143 min_valence_val = valence_val;
144 }
145 if (valence_val > max_valence_val) {
146 max_valence_val = valence_val;
147 }
148
149 });
150
151 console.log(`AVRecommendations Arousal limits: ${min_arousal_val} <-> ${max_arousal_val}`);
152 console.log(`AVRecommendations Valence limits: ${min_valence_val} <-> ${max_valence_val}`);
153
154 var av_cluster_x_org = (min_arousal_val + max_arousal_val)/2.0;
155 var av_cluster_y_org = (min_valence_val + max_valence_val)/2.0;
156
157 var av_cluster_x_delta = Math.abs(min_arousal_val - max_arousal_val)/2.0;
158 var av_cluster_y_delta = Math.abs(min_valence_val - max_valence_val)/2.0;
159
160 //var av_cluster_rad = Math.max(Math.max(av_cluster_x_delta,av_cluster_y_delta),3.0);
161
162 var av_cluster_rad = Math.max(av_cluster_x_delta,av_cluster_y_delta);
163 var disp_cluster_coord = av_to_display_coord(av_cluster_rad,av_cluster_rad);
164 var disp_cluster_rad = disp_cluster_coord.x - av_chart_disp_x_mid; // **** **** ????
165 var disp_cluster_rad_maxed = Math.max(disp_cluster_rad,3.0);
166
167 var disp_coord = av_to_display_coord(av_cluster_x_org,av_cluster_y_org);
168 var disp_x = disp_coord.x;
169 var disp_y = disp_coord.y;
170
171 var av_chart_canvas = document.getElementById('av-chart-canvas');
172
173 var ctx = av_chart_canvas.getContext("2d");
174 ctx.clearRect(0, 0, av_chart_canvas.width, av_chart_canvas.height);
175 ctx.beginPath();
176 ctx.arc(disp_x/2, disp_y/2, disp_cluster_rad_maxed/2, 0, 2*Math.PI, false);
177 ctx.fillStyle = 'rgba(50,50,128,0.5)';
178 ctx.fill();
179 ctx.lineWidth = 1;
180 ctx.strokeStyle = 'rgba(0,0,160,0.5)';
181 ctx.stroke();
182
183}
184
185function ajaxUpdateDocumentDisplayed()
186{
187 var $resultsTable = $('#resultsTable');
188
189 var $resultsTable_trs = $resultsTable.find('tr');
190
191 if ($resultsTable_trs.length>0) {
192 var $resultsTable_tr1 = $resultsTable_trs.first();
193 var div_doc_id = $resultsTable_tr1.attr('id')
194 var doc_id = div_doc_id.substring(3);
195
196 console.log("first AV recommendation doc id = " + doc_id);
197
198 var $av_recommendation = $resultsTable_tr1.find('.AVRecommendation').first();
199
200 var new_doc_url = $av_recommendation.attr('href');
201 var new_doc_url_parts = new_doc_url.split("#");
202
203 var new_doc_metadata_url = new_doc_url_parts[0] + "&excerptid=metadata-documenttext#" + new_doc_url_parts[1];
204 var new_doc_playervisual_url = new_doc_url_parts[0] + "&excerptid=playervisual-documenttext#" + new_doc_url_parts[1];
205
206 //console.log("new doc metadata url = " + new_doc_metadata_url);
207 //console.log("new doc playervisual url = " + new_doc_playervisual_url);
208
209
210 // current URL ends with collect/amc-essentia/document/ds_51017_15513?p.frameOffset=...
211 // Need to remove old '/document/ds_...' and replace with new doc
212
213 const current_doc_url_str = window.location.href;
214
215 console.log(current_doc_url_str);
216 var push_doc_url_str = current_doc_url_str.replace(/document\/\w+\?/,"document/"+doc_id+"?");
217 console.log("new doc url str = " + push_doc_url_str);
218 const push_doc_url = new URL(push_doc_url_str);
219
220 var frameOffset = $av_recommendation.data("frameoffset");
221 push_doc_url.searchParams.set('p.frameOffset', frameOffset);
222 push_doc_url.searchParams.set('d', doc_id);
223 window.history.pushState({}, '', push_doc_url);
224 //window.history.replaceState({}, '', push_doc_url);
225
226
227
228 $('#metadata-documenttext').css("cursor","wait");
229 $('#playervisual-documenttext').css("cursor","wait");
230
231 $.ajax({
232 method: "GET",
233 url: new_doc_metadata_url,
234 })
235 .always(function() {
236 $('#metadata-documenttext').css("cursor","revert");
237 })
238
239 .fail(function(jqXHR,textStatus) {
240 console.error( "metadata-documenttext ajax request failed: " + textStatus);
241 })
242 .done(function(html_result) {
243 $('#metadata-documenttext').replaceWith(html_result);
244 });
245
246
247 $.ajax({
248 method: "GET",
249 url: new_doc_playervisual_url,
250 })
251 .always(function() {
252 $('#playervisual-documenttext').css("cursor","revert");
253 })
254 .fail(function(jqXHR,textStatus) {
255 console.error( "playervisual-documenttext equest failed: " + textStatus);
256 })
257 .done(function(html_result) {
258
259 $('#playervisual-documenttext').replaceWith(html_result);
260
261 gs.cgiParams['p_frameOffset'] = frameOffset;
262 gs.documentMetadata["assocfilepath"] = $('#ajax-loaded-assocfilepath').text();
263
264 initPlayerVisual();
265 initWavesurferPlayer();
266
267 wavesurfer.seekTo(frameOffset/AMC_SONG_DURATION);
268
269 postInitWavesurfer(wavesurfer);
270
271
272 });
273
274
275 $resultsTable_tr1.slideUp();
276
277 //var arousal_val = $av_recommendation.data("arousalval");
278 //var valence_val = $av_recommendation.data("valenceval");
279
280
281 }
282
283}
284
285function submitAVRecommendation(form, arousal_val,valence_val, current_time)
286{
287 form.elements["s1.arousal"].value = arousal_val;
288 form.elements["s1.valence"].value = valence_val;
289
290 var args = {
291 "a": "q",
292 "rt": "rd",
293 "s": "AudioQuery",
294 "sa": "",
295 "c": gs.cgiParams["c"],
296 "q": gs.cgiParams["d"],
297 "s1.query": gs.cgiParams["d"],
298 "s1.maxDocs": form.elements["s1.maxDocs"].value,
299 "s1.hitsPerPage": form.elements["s1.hitsPerPage"].value,
300 "s1.offset": form.elements["s1.offset"].value,
301 "s1.length": form.elements["s1.length"].value,
302 "s1.arousal": arousal_val,
303 "s1.valence": valence_val,
304 "startPage": 1,
305 "excerptid": "resultsArea"
306 };
307
308
309 var url = "https://mars.so-we-must-think.space/greenstone3/library";
310
311 $('#recommendationArea').css("cursor","wait");
312 $('#resultsAreaDiv').slideDown();
313 $('#resultsAreaDiv').html("Retrieving recommendation ...");
314
315 $.ajax({
316 method: "GET",
317 url: url,
318 data: args
319 })
320 .always(function() {
321 $('#recommendationArea').css("cursor","revert");
322 })
323 .fail(function(jqXHR,textStatus) {
324 console.error( "Request failed: " + textStatus);
325 })
326 .done(function(html_result) {
327 $('#resultsAreaDiv').html("<div>Recommendations:</div>"+html_result);
328 $('#av-chart-div').show();
329
330 AVPlotRecommendations();
331
332 if (current_time) {
333 const updated_url = new URL(window.location);
334 updated_url.searchParams.set('p.frameOffset', current_time);
335 //window.history.pushState({}, '', url + "?p.frameOffset=" + current_time);
336 //window.history.pushState({}, '', updated_url);
337 window.history.replaceState({}, '', updated_url);
338 }
339 else {
340 // The result of a click on the AV-chart
341 ajaxUpdateDocumentDisplayed();
342 }
343 });
344
345 // stop submit
346 return false;
347
348 // force GET method request to go ahead
349 //return true;
350}
351
352
353
354function recommendFromStart()
355{
356 currentPosIsZero = true;
357 $('#makeRecommendationFrom').html("Based on the start of this musical/sound art work: ");
358}
359
360
361function recommendFromPos()
362{
363 currentPosIsZero = false;
364 $('#makeRecommendationFrom').html("Based on the current timeline position of this musical/sound art work: ");
365}
366
367
368
369function postInitWavesurfer(wavesurfer)
370{
371 console.log("postInitWavesurfer called with wavesufer = " + wavesurfer);
372
373 wavesurfer.load(gs.variables.mp3url);
374
375 wavesurfer.on('audioprocess', function () {
376 var current_time = wavesurfer.getCurrentTime();
377 var current_time_rounded = Math.round(current_time * 10) / 10
378 var current_time_rounded = current_time.toFixed(1);
379 $('#audioCurrentPos').html(current_time_rounded + " secs");
380
381 if (current_time == 0) {
382 recommendFromStart();
383 }
384 else if ((currentPosIsZero) && (current_time > 0)) {
385 recommendFromPos();
386 }
387 displayClampedCurrentTimeAndAV();
388
389 if (isRecordingUserAV) {
390 appendRecordedUserAVVals();
391 }
392 });
393
394 wavesurfer.on('finish', function () {
395 // auto stop RecordingUserAV
396 av_chart_record_finish();
397 });
398
399 wavesurfer.on('ready', function () {
400
401 console.log("**** wavesurfer ready()");
402
403 if ('p_frameOffset' in gs.cgiParams) {
404 var frameOffset = gs.cgiParams['p_frameOffset'];
405 //console.log("**** starting play @ " + frameOffset);
406 //wavesurfer.play(frameOffset);
407 //console.log("**** setting play seek @ " + frameOffset);
408 wavesurfer.seekTo(frameOffset/AMC_SONG_DURATION);
409 recommendFromPos();
410 displayClampedCurrentTimeAndAV();
411
412 console.log("av_document.js: **** Keeping 'currentPosRunup' at 0 for now!");
413 /*if (frameOffset>1.0) {
414 currentPosRunup = 1.0; // 1 second
415 }*/
416 }
417 else {
418 recommendFromStart();
419 displayClampedCurrentTimeAndAV();
420 }
421 });
422
423}
424
425/*
426
427y-top: 146
428
429Centre: 412,443
430
431y-bot:735
432
433x-left: 120
434
435x-right 710
436
437
438Full image:
439 911 x 825 pxs
440
441
442
443 AV centre: 412,443
444
445delta x/y: from centre: 292 <-> 298
446-1 <-> 0 <-> +1
447 295 295
448
449
450SVG:
451
452 241 x 248
453 scaled
454 0.26458
455
456*/
457
458var av_chart_orig_r = 295;
459
460var av_chart_orig_x_org = 412;
461var av_chart_orig_y_org = 443;
462
463var av_chart_orig_x_dim = 911;
464var av_chart_orig_y_dim = 825;
465
466var av_chart_disp_x_dim = 300; // **** av-chart-width
467var av_chart_disp_x_mid = av_chart_disp_x_dim/2.0;
468
469var av_chart_scale = av_chart_orig_x_dim / av_chart_disp_x_dim;
470
471function display_to_av_coord(disp_x,disp_y)
472{
473 var scaled_disp_x = disp_x * av_chart_scale;
474 var scaled_disp_y = disp_y * av_chart_scale;
475
476 var orig_av_x = scaled_disp_x - av_chart_orig_x_org;
477 var orig_av_y = -1 * (scaled_disp_y - av_chart_orig_y_org); // flip y axis
478
479 var av_x = orig_av_x / av_chart_orig_r;
480 var av_y = orig_av_y / av_chart_orig_r;
481
482 var capped_av_x = Math.max(Math.min(av_x,1.0),-1.0);
483 var capped_av_y = Math.max(Math.min(av_y,1.0),-1.0);
484
485 return { "x": capped_av_x, "y": capped_av_y };
486}
487
488
489function av_to_display_coord(av_x,av_y)
490{
491 //var capped_av_x = Math.max(Math.min(av_x,1.0),-1.0);
492 //var capped_av_y = Math.max(Math.min(av_y,1.0),-1.0);
493
494 // transform the coord system from av [-1,1] to be the original SVG AV chart size
495 var orig_av_x = av_x * av_chart_orig_r;
496 var orig_av_y = av_y * av_chart_orig_r;
497
498 // tranform the coord system to be expressed from the top-left as the origin
499 var scaled_disp_x = orig_av_x + av_chart_orig_x_org;
500 var scaled_disp_y = (-1 * orig_av_y) + av_chart_orig_y_org;
501
502 var disp_x = scaled_disp_x / av_chart_scale;
503 var disp_y = scaled_disp_y / av_chart_scale;
504
505 return { "x": disp_x, "y": disp_y };
506}
507
508
509function av_chart_click(elem,e)
510{
511 var offset = $(elem).offset();
512
513 var elem_x_org = offset.left;
514 var elem_y_org = offset.top;
515
516 var disp_x = e.pageX - elem_x_org;
517 var disp_y = e.pageY - elem_y_org;
518
519 console.log(`(disp_x,disp_y) = (${disp_x},${disp_y})`);
520 var av_coord = display_to_av_coord(disp_x,disp_y);
521 var capped_av_x = av_coord.x;
522 var capped_av_y = av_coord.y;
523
524 console.log(`av x,y: (${capped_av_x},${capped_av_y})`);
525
526 //var disp_coord = av_to_display_coord(capped_av_x,capped_av_y);
527 //var reconverted_disp_x = disp_coord.x;
528 //var reconverted_disp_y = disp_coord.y;
529
530 //console.log(`reconverted (disp_x,disp_y) = (${reconverted_disp_x},${reconverted_disp_y})`);
531
532 var form = $('#av-query-form')[0];
533
534 var clamped_current_time = 6;
535 AVEnsurePaused();
536 weka_segment = Math.round(clamped_current_time/3) * 3;
537 console.log("**** av_chart_click(): dynamically setting form[s1.offset] = " + weka_segment);
538 form.elements["s1.offset"].value = weka_segment;
539
540 submitAVRecommendation(form, capped_av_x, capped_av_y, null);
541
542}
543
544var hasExistingUserRecording = false;
545
546var isRecordingUserAV = false;
547
548var recordedUserArousalVal = 0;
549var recordedUserValenceVal = 0;
550var recordedUserCanvasX = 0;
551var recordedUserCanvasY = 0;
552
553var recordedAVs = {
554 predArousal: [],
555 predValence: [],
556 userArousal: [],
557 userValence: [],
558 diffArousal: [],
559 diffValence: [],
560
561 canvasX: [],
562 canvasY: []
563
564};
565
566function resetRecordedAVs() {
567 recordedAVs.predArousal = [];
568 recordedAVs.predValence = [];
569 recordedAVs.userArousal = [];
570 recordedAVs.userValence = [];
571 recordedAVs.diffArousal = [];
572 recordedAVs.diffValence = [];
573
574 recordedAVs.canvasX = [];
575 recordedAVs.canvasY = [];
576}
577
578function appendRecordedUserAVVals()
579{
580 var current_time = wavesurfer.getCurrentTime();
581
582 // Predicted AV vals
583 var pred_arousal_val = parseFloat($('#arousal-val').text());
584 var pred_valence_val = parseFloat($('#valence-val').text());
585
586 var user_arousal_val = recordedUserArousalVal;
587 var user_valence_val = recordedUserValenceVal;
588
589 var diff_arousal_val = pred_arousal_val - user_arousal_val;
590 var diff_valence_val = pred_valence_val - user_valence_val;
591
592 recordedAVs.predArousal.push(pred_arousal_val);
593 recordedAVs.predValence.push(pred_valence_val);
594
595 recordedAVs.userArousal.push(user_arousal_val);
596 recordedAVs.userValence.push(user_valence_val);
597
598 recordedAVs.diffArousal.push(diff_arousal_val);
599 recordedAVs.diffValence.push(diff_valence_val);
600
601 recordedAVs.canvasX.push(recordedUserCanvasX);
602 recordedAVs.canvasY.push(recordedUserCanvasY);
603
604
605 var av_canvas = document.getElementById("av-chart-canvas-unlabeled");
606 var av_context = av_canvas.getContext("2d");
607
608 av_context.fillStyle = "rgba(190,0,0,0.05)";
609 //av_context.fillStyle = "rgba(130,0,0,0.1)";
610 //av_context.fillStyle = "rgba(78,196,108,0.1)";
611 av_context.beginPath();
612 av_context.arc(recordedUserCanvasX, recordedUserCanvasY, 5, 0, 2 * Math.PI);
613 av_context.fill();
614
615}
616
617function displayPersonalBias(overall_personal_bias_rec)
618{
619 var bias_arousal_2dp = overall_personal_bias_rec['bias-arousal'].toFixed(2);
620 var bias_valence_2dp = overall_personal_bias_rec['bias-valence'].toFixed(2);
621
622 $('#overall-personal-bias')
623 .html("Personal Bias (Arousal,Valence):"
624 + " ("
625 + bias_arousal_2dp
626 + ","
627 + bias_valence_2dp
628 + ")");
629
630}
631
632function clearRecordedUserAVChart()
633{
634 var doc_storage_key = gs.cgiParams.c + "." + gs.cgiParams.d;
635
636 var doc_personal_bias_arousal = 0.0;
637 var doc_personal_bias_valence = 0.0;
638
639 var recordedAVs_str = window.localStorage.getItem(doc_storage_key);
640 if (recordedAVs_str) {
641 recordedAVs = JSON.parse(recordedAVs_str);
642
643 doc_personal_bias_arousal = recordedAVs.personalBiasArousal;
644 doc_personal_bias_valence = recordedAVs.personalBiasValence;
645
646 }
647
648 window.localStorage.removeItem(doc_storage_key);
649
650 var overall_personal_bias_key = "mars.overall-personal-bias";
651
652 var overall_personal_bias_rec_str = window.localStorage.getItem(overall_personal_bias_key);
653
654 var overall_personal_bias_rec;
655 if (overall_personal_bias_rec_str) {
656 overall_personal_bias_rec = JSON.parse(overall_personal_bias_rec_str);
657
658 overall_personal_bias_rec['bias-arousal'] -= doc_personal_bias_arousal;
659 overall_personal_bias_rec['bias-valence'] -= doc_personal_bias_valence;
660 }
661
662 window.localStorage.setItem(overall_personal_bias_key, JSON.stringify(overall_personal_bias_rec));
663
664 displayPersonalBias(overall_personal_bias_rec);
665
666
667 // Now proceed to visually clear the av-chart-canvas
668 var av_canvas = document.getElementById("av-chart-canvas-unlabeled");
669 var av_context = av_canvas.getContext("2d");
670 av_context.clearRect(0, 0, av_canvas.width, av_canvas.height);
671
672 resetRecordedAVs();
673 hasExistingUserRecording = false;
674}
675
676function renderRecordedUserAVVals()
677{
678
679 var doc_storage_key = gs.cgiParams.c + "." + gs.cgiParams.d;
680
681 var recordedAVs_str = window.localStorage.getItem(doc_storage_key);
682 if (recordedAVs_str) {
683 recordedAVs = JSON.parse(recordedAVs_str);
684
685 var av_canvas = document.getElementById("av-chart-canvas-unlabeled");
686 var av_context = av_canvas.getContext("2d");
687
688 var num_canvas_coords = recordedAVs.canvasX.length;
689
690 for (var i=0; i<num_canvas_coords; i++) {
691
692 var canvas_x = recordedAVs.canvasX[i];
693 var canvas_y = recordedAVs.canvasY[i];
694
695 av_context.fillStyle = "rgba(190,0,0,0.05)";
696 //av_context.fillStyle = "rgba(130,0,0,0.1)";
697 //av_context.fillStyle = "rgba(78,196,108,0.1)";
698 av_context.beginPath();
699 av_context.arc(canvas_x, canvas_y, 5, 0, 2 * Math.PI);
700 av_context.fill();
701 }
702
703 hasExistingUserRecording = true;
704 }
705 else {
706 hasExistingUserRecording = false;
707 }
708
709}
710
711function crsc_popup_close($elem)
712{
713 $elem.parents('.dialog-ovelay').fadeOut(500, function () {
714 $elem.remove();
715 });
716}
717
718function crsc_popup_cancel_callback()
719{
720 console.log("Canceling RecordingUserAV");
721
722 var av_canvas = document.getElementById("av-chart-canvas-unlabeled");
723 var av_context = av_canvas.getContext("2d");
724
725 av_context.clearRect(0, 0, av_canvas.width, av_canvas.height);
726 resetRecordedAVs();
727 hasExistingUserRecording = false;
728
729 crsc_popup_close($(this));
730
731
732 if ('p_frameOffset' in gs.cgiParams) {
733 var frameOffset = gs.cgiParams['p_frameOffset'];
734 wavesurfer.seekTo(frameOffset/AMC_SONG_DURATION);
735 recommendFromPos();
736 displayClampedCurrentTimeAndAV();
737 }
738 else {
739 AVSeekTo(0);
740 }
741
742}
743
744function crsc_popup_resume_callback()
745{
746 console.log("Resuming RecordingUserAV");
747 isRecordingUserAV = true;
748 crsc_popup_close($(this));
749 AVPlay();
750}
751
752function crsc_popup_save_callback()
753{
754 console.log("Saving to your browser RecordingUserAV");
755 crsc_popup_close($(this));
756
757 var num_av_vals = recordedAVs.userArousal.length;
758
759 var doc_personal_bias_arousal = 0;
760 var doc_personal_bias_valence = 0;
761
762 for (var i=0; i<num_av_vals; i++) {
763 doc_personal_bias_arousal += recordedAVs.diffArousal[i];
764 doc_personal_bias_valence += recordedAVs.diffValence[i];
765 }
766
767 doc_personal_bias_arousal /= num_av_vals;
768 doc_personal_bias_valence /= num_av_vals;
769
770 recordedAVs.personalBiasArousal = doc_personal_bias_arousal;
771 recordedAVs.personalBiasValence = doc_personal_bias_valence;
772
773 var doc_storage_key = gs.cgiParams.c + "." + gs.cgiParams.d;
774 window.localStorage.setItem(doc_storage_key,JSON.stringify(recordedAVs));
775
776 var overall_personal_bias_key = "mars.overall-personal-bias";
777
778 var overall_personal_bias_rec_str = window.localStorage.getItem(overall_personal_bias_key);
779
780 var overall_personal_bias_rec;
781 if (overall_personal_bias_rec_str) {
782 overall_personal_bias_rec = JSON.parse(overall_personal_bias_rec_str);
783
784 }
785 else {
786 overall_personal_bias_rec = { 'bias-arousal': 0.0, 'bias-valence': 0.0 };
787 }
788
789 overall_personal_bias_rec['bias-arousal'] += doc_personal_bias_arousal;
790 overall_personal_bias_rec['bias-valence'] += doc_personal_bias_valence;
791
792 window.localStorage.setItem(overall_personal_bias_key, JSON.stringify(overall_personal_bias_rec));
793 displayPersonalBias(overall_personal_bias_rec);
794
795 hasExistingUserRecording = true;
796}
797
798
799
800function av_chart_record_click(elem,event)
801{
802 if (hasExistingUserRecording) {
803 return;
804 }
805
806 var av_chart_position_info = get_av_chart_unlabeled_position_info(elem,event);
807
808 var av_inside = av_chart_position_info.av_inside;
809 if (av_inside) {
810 recordedUserValenceVal = av_chart_position_info.valence_val;
811 recordedUserArousalVal = av_chart_position_info.arousal_val;
812
813 recordedUserCanvasX = av_chart_position_info.canvas_x;
814 recordedUserCanvasY = av_chart_position_info.canvas_y;
815
816
817 if (isRecordingUserAV == false) {
818 isRecordingUserAV = true;
819
820 if (!wavesurfer.backend.isPaused()) {
821 console.log("Starting recording user's AV values");
822 AVSeekTo(0);
823 }
824 else {
825 console.log("Resuming recording user's AV values");
826 }
827 AVPlay()
828 }
829 else {
830 AVEnsurePaused();
831 // Have stopped recording
832 // => Store recorded values into LocalStorage
833 isRecordingUserAV = false;
834
835
836 CancelResumeSaveConfirm('Customised AV Recording', 'Would you like to save this AV Recording?',
837 'Cancel and Start Over', 'Resume Recording', "Save",
838 crsc_popup_cancel_callback, crsc_popup_resume_callback, crsc_popup_save_callback
839 );
840 }
841 }
842}
843
844function av_chart_record_finish(elem,event)
845{
846 // Restore play/pause toggle button back to 'ready to play'
847 AVSeekTo(0);
848 $('#ws-play-icon' ).show();
849 $('#ws-pause-icon').hide();
850 $('#ws-playpause-label').html("Play");
851
852 if (isRecordingUserAV == true) {
853 isRecordingUserAV = false;
854
855 CancelResumeSaveConfirm('Customised AV Recording', 'Would you like to save this AV Recording?',
856 'Cancel and Start Over', null, "Save",
857 crsc_popup_cancel_callback, null, crsc_popup_save_callback
858 );
859 }
860
861
862}
863
864
865function initPlayerVisual() {
866
867 const doc_url = new URL(window.location);
868 var renderWave = doc_url.searchParams.get('renderWave');
869
870 if (renderWave && (renderWave == 1)) {
871 // showing the waveform => offer link to spectrogram
872 const sts_url = new URL(window.location);
873 sts_url.searchParams.set('renderWave', 0);
874 $('#switch-to-spectrogram').attr("href",sts_url);
875 $('#switch-to-waveform').hide();
876
877 $('#av-timelinebar-help').hide();
878 }
879 else {
880 // show the spectrogram => offer link to waveform
881
882 const stw_url = new URL(window.location);
883 stw_url.searchParams.set('renderWave', 1);
884 $('#switch-to-waveform').attr("href",stw_url);
885 $('#switch-to-spectrogram').hide();
886
887 $('#av-timelinebar-help').show();
888 }
889}
890
891function showAVChart()
892{
893 if ($('#av-chart-outerdiv').is(":hidden")) {
894 var wave_form_height = $('#waveform').height();
895
896 $('#waveform')
897 .css("transform-origin","top")
898 .css("transform","scale(1.0,0.5)")
899 .height(wave_form_height/2.0);
900
901
902 $('#av-chart-img-unlabeled').hide();
903 $('#av-chart-canvas-unlabeled').hide();
904 $('#clear-user-av-chart-rtab').hide();
905 $('#av-chart-img').show();
906 $('#av-chart-canvas').show();
907
908 $('#start-av-recommendation').show();
909 $('#start-av-recording').hide();
910
911 $('#av-chart-outerdiv').slideDown();
912 $('#show-av-chart').hide();
913 $('#hide-av-chart').show();
914 }
915
916 return false;
917}
918
919function hideAVChart()
920{
921 if ($('#av-chart-outerdiv').is(':visible')) {
922
923 // Slide up the chart display area
924 $('#av-chart-outerdiv').slideUp(function() {
925
926 // Reset what is being display to the default (the av-chart, not the record one)
927 $('#av-chart-img-unlabeled').hide();
928 $('#av-chart-chart-unlabeled').hide();
929 $('#av-chart-img').show();
930 $('#av-chart-canvas').show();
931
932 $('#start-av-recommendation').show();
933 $('#start-av-recording').hide();
934 });
935
936 // Fix up the labels
937
938 $('#hide-av-chart').hide();
939 $('#clear-user-av-chart-rtab').hide();
940 $('#show-av-chart').show();
941
942
943 // Restore the renderWave/Frequency height
944
945 const doc_url = new URL(window.location);
946 var renderWave = doc_url.searchParams.get('renderWave');
947
948 if (renderWave && renderWave == 1) {
949 $('#waveform').find('wave canvas').css("display","block");
950 }
951 var wave_form_height = $('#waveform').height();
952 $('#waveform')
953 .height(wave_form_height*2.0)
954 .css("transform","scale(1.0,1.0)")
955 }
956
957 return false;
958}
959
960
961
962function loginToRecord()
963{
964
965 if (gs.variables.loggedInUsername == "") {
966 // Example login redirect
967 // https://www.greenstone.org/greenstone3/library?a=p&sa=login&redirectURL=library%3Fa=p%26sa=about%26c=kjcoll%26favouritebasket=on
968
969 var this_url = new URL(window.location)
970 this_url.searchParams.set('showRecordAV', '1');
971 var encoded_this_url = encodeURIComponent(this_url.href);
972
973 var base_url = window.location.origin + window.location.pathname;
974
975 window.location.href = base_url + "?a=p&sa=login&redirectURL=" + encoded_this_url;
976 }
977 else {
978 /*
979 var av_chart_svg = document.getElementById("av-chart-svg");
980 //get the inner DOM of alpha.svg
981 var svgDoc = av_chart_svg.contentDocument;
982
983 //get the inner element by id
984 var av_labels = svgDoc.getElementClass("av-label");
985 var $av_labels = $(av_labels);
986
987 $av_labels.css("display","none");
988 */
989
990 //console.log("Away to hide standard av-chart-img and show av-chart-img-unlabeled");
991
992
993
994 if ($('#av-chart-outerdiv').is(":hidden")) {
995
996 const doc_url = new URL(window.location);
997 var renderWave = doc_url.searchParams.get('renderWave');
998
999 if (renderWave && renderWave == 1) {
1000 $('#waveform').find('wave canvas').css("display","none");
1001 }
1002 var wave_form_height = $('#waveform').height();
1003
1004 $('#waveform')
1005 .css("transform-origin","top")
1006 .css("transform","scale(1.0,0.5)")
1007 .height(wave_form_height/2.0);
1008 }
1009
1010 // Ensure the RecordAV Chart if showing
1011 if ($('#av-chart-img-unlabeled').is(":hidden")) {
1012
1013 // Ensure it is the RecordAV chart that is shown
1014 $('#av-chart-img').hide();
1015 $('#av-chart-canvas').hide();
1016 $('#av-chart-img-unlabeled').show();
1017 $('#av-chart-canvas-unlabeled').show();
1018 $('#clear-user-av-chart-rtab').show();
1019
1020 $('#start-av-recommendation').hide();
1021 $('#start-av-recording').show();
1022
1023 // Which is then slid-down
1024 $('#av-chart-outerdiv').slideDown();
1025
1026 // Fix up the control label
1027 $('#show-av-chart').hide();
1028 $('#hide-av-chart').show();
1029 }
1030
1031 }
1032
1033}
1034
1035// circ_.* parameters express position of the circle, relative to the top-left position of the elem
1036function mouse_xy_to_av(elem,event,circ_x_org,circ_y_org,circ_radius)
1037{
1038 var relX = event.pageX - $(elem).offset().left;
1039 var relY = event.pageY - $(elem).offset().top;
1040
1041 var valenceVal = -1 * (circ_x_org - relX) / circ_radius;
1042 var arousalVal = +1 * (circ_y_org - relY) / circ_radius;
1043
1044 var avRadius = Math.sqrt(valenceVal*valenceVal + arousalVal*arousalVal);
1045
1046 return { 'valence': valenceVal, 'arousal': arousalVal, 'avRadius': avRadius };
1047}
1048
1049
1050// Need to be very careful with order of valenceVal and arousalVal
1051// Here they are ordered to match x,y
1052
1053function av_to_canvas_xy__UNTESTED(valenceVal, arousalVal, circ_x_org,circ_y_org,circ_radius)
1054{
1055 //var canvasX = (valenceVal * circ_radius) - circ_x_org; // effectively "times -1"
1056 var canvasX = circ_x_org - (valenceVal * circ_radius);
1057 var canvasY = circ_y_org - (arousalVal * circ_radius);
1058
1059
1060 return { 'canvasX': canvasX, 'canvasY': canvasY };
1061}
1062
1063function get_av_chart_unlabeled_position_info(elem,event)
1064{
1065 var circ_x_org = 183;
1066 var circ_y_org = 194;
1067 var circ_radius = 130;
1068
1069 var av_rec = mouse_xy_to_av(elem,event,circ_x_org,circ_y_org,circ_radius);
1070
1071 var valence_val = av_rec.valence;
1072 var arousal_val = av_rec.arousal;
1073 var av_radius = av_rec.avRadius;
1074
1075 var av_inside = (av_radius <= 1.0);
1076
1077 var return_rec = { 'av_inside': av_inside };
1078
1079 if (av_inside) {
1080 return_rec.arousal_val = arousal_val;
1081 return_rec.valence_val = valence_val;
1082
1083 var canvas_x = event.pageX - $(elem).offset().left;
1084 var canvas_y = event.pageY - $(elem).offset().top;
1085
1086 return_rec.canvas_x = canvas_x;
1087 return_rec.canvas_y = canvas_y;
1088 }
1089
1090 return return_rec;
1091}
1092
1093$(document).ready(function() {
1094
1095 console.log("AV Recording now always possible, without logging in (pseudo username: mars)");
1096 gs.variables.loggedInUsername = "mars";
1097 //console.log(`loggedInUsername = '${gs.variables.loggedInUsername}'`);
1098 if (gs.variables.loggedInUsername != "") {
1099 // Logged in
1100 $('#login-to-record-av').hide();
1101 $('#record-av').show();
1102
1103 var this_url = new URL(window.location);
1104 var show_record_av = this_url.searchParams.get("showRecordAV");
1105
1106
1107 if (show_record_av && show_record_av == 1) {
1108 loginToRecord();
1109 }
1110
1111 renderRecordedUserAVVals();
1112 }
1113
1114 //var arg_add = (window.location.search != "") ? "&" : "?";
1115 //$('#record-av-logout').attr("href",window.location.href + arg_add + "logout=");
1116
1117 var $record_av_logout = $('#record-av-logout');
1118
1119 //record_av_logout_url = new URL($record_av_logout.attr("href"));
1120 record_av_logout_url = new URL(window.location);
1121 record_av_logout_url.searchParams.set('logout', '');
1122 record_av_logout_url.searchParams.set('showRecordAV', '0');
1123 $record_av_logout.attr("href",record_av_logout_url.href);
1124
1125 initPlayerVisual();
1126
1127 var overall_personal_bias_key = "mars.overall-personal-bias";
1128 var overall_personal_bias_rec_str = window.localStorage.getItem(overall_personal_bias_key);
1129
1130 var overall_personal_bias_rec;
1131 if (overall_personal_bias_rec_str) {
1132 overall_personal_bias_rec = JSON.parse(overall_personal_bias_rec_str);
1133 displayPersonalBias(overall_personal_bias_rec);
1134 }
1135
1136 $('#av-chart-img').on("click",function(event) {
1137 av_chart_click(this,event)
1138 });
1139
1140 $('#av-chart-img').on("mousemove",function(event) {
1141 var circ_x_org = 137;
1142 var circ_y_org = 145;
1143 var circ_radius = 97;
1144
1145 var av_rec = mouse_xy_to_av(this,event,circ_x_org,circ_y_org,circ_radius);
1146
1147 var recValenceVal = av_rec.valence;
1148 var recArousalVal = av_rec.arousal;
1149 var avRadius = av_rec.avRadius;
1150
1151 var recAVInside = (avRadius <= 1.0);
1152
1153 if (recAVInside) {
1154
1155 var roundedA = Math.round(recArousalVal * 100) / 100
1156 var roundedV = Math.round(recValenceVal * 100) / 100
1157
1158 var relBoxCoords = "AV coord: (" + roundedV + "," + roundedA + ")";
1159 $("#av-recording-coords").text(relBoxCoords);
1160 }
1161
1162 if (recAVInside != prevRecAVInside) {
1163 if (recAVInside) {
1164 // crosshairs on
1165 $('#av-chart-img').css("cursor","crosshair");
1166 }
1167 else {
1168 // revert
1169 $('#av-chart-img').css("cursor","default");
1170 }
1171 }
1172
1173 prevRecAVInside = recAVInside;
1174 });
1175
1176
1177
1178 $('#av-chart-img-unlabeled').on("click",function(event) {
1179 av_chart_record_click(this,event);
1180 });
1181
1182
1183 var prevRecAVInside = true;
1184
1185 $('#av-chart-img-unlabeled').on("mouseover",function(event) {
1186 //console.log("**** mouseenter")
1187 var av_chart_position_info = get_av_chart_unlabeled_position_info(this,event);
1188
1189 var av_inside = av_chart_position_info.av_inside;
1190
1191 if (av_inside) {
1192 //console.log("**** inside")
1193
1194 // crosshairs on
1195 var crosshair_record_url = gs.variables.collImagesURL + "/crosshair-cursor-red32.png";
1196 $('#av-chart-img-unlabeled').css("cursor","url('"+crosshair_record_url+"') 16 16, crosshair");
1197 }
1198 else {
1199 // revert
1200 $('#av-chart-img-unlabeled').css("cursor","default");
1201 }
1202 });
1203
1204
1205 $('#av-chart-img-unlabeled').on("mousemove",function(event) {
1206/*
1207 var circ_x_org = 183;
1208 var circ_y_org = 194;
1209 var circ_radius = 130;
1210
1211 var av_rec = mouse_xy_to_av(this,event,circ_x_org,circ_y_org,circ_radius);
1212
1213 var recValenceVal = av_rec.valence;
1214 var recArousalVal = av_rec.arousal;
1215 var avRadius = av_rec.avRadius;
1216
1217 var recAVInside = (avRadius <= 1.0);
1218*/
1219
1220 var av_chart_position_info = get_av_chart_unlabeled_position_info(this,event);
1221
1222 var recValenceVal = av_chart_position_info.valence_val;
1223 var recArousalVal = av_chart_position_info.arousal_val;
1224 var recAVInside = av_chart_position_info.av_inside;
1225
1226 if (recAVInside) {
1227
1228 var roundedA = Math.round(recArousalVal * 100) / 100
1229 var roundedV = Math.round(recValenceVal * 100) / 100
1230
1231 var relBoxCoords = "AV coord: (" + roundedV + "," + roundedA + ")";
1232 $("#av-recording-coords").text(relBoxCoords);
1233 }
1234
1235 if ((recAVInside) && (isRecordingUserAV)) {
1236 //var xy_rec = av_to_canvas_xy(recValenceVal,recArousalVal, circ_x_org,circ_y_org, circ_radius);
1237 //var canvas_x = xy_rec.canvasX;
1238 //var canvas_y = xy_rec.canvasY;
1239
1240 //var canvas_x = event.pageX - $(this).offset().left;
1241 //var canvas_y = event.pageY - $(this).offset().top;
1242
1243 var canvas_x = av_chart_position_info.canvas_x;
1244 var canvas_y = av_chart_position_info.canvas_y;
1245
1246 //console.log("**** canvas x,y = " + canvas_x + " , " + canvas_y);
1247
1248 recordedUserArousalVal = recArousalVal;
1249 recordedUserValenceVal = recValenceVal;
1250
1251 recordedUserCanvasX = canvas_x;
1252 recordedUserCanvasY = canvas_y;
1253
1254 }
1255
1256 if (hasExistingUserRecording) {
1257 $('#av-chart-img-unlabeled').css("cursor","default");
1258 }
1259 else {
1260 if (recAVInside != prevRecAVInside) {
1261 if (recAVInside) {
1262 // crosshairs on
1263 var crosshair_record_url = gs.variables.collImagesURL + "/crosshair-cursor-red32.png";
1264 $('#av-chart-img-unlabeled').css("cursor","url('"+crosshair_record_url+"') 16 16, crosshair");
1265 }
1266 else {
1267 // revert
1268 $('#av-chart-img-unlabeled').css("cursor","default");
1269 }
1270 }
1271 }
1272
1273 prevRecAVInside = recAVInside;
1274 });
1275
1276});
1277
1278// A circumplex model of affect.
1279// https://psycnet.apa.org/record/1981-25062-001
1280
1281
1282// Russell, J. A. (1980). A circumplex model of affect. Journal of Personality and Social Psychology, 39(6), 1161–1178.
1283// https://doi.org/10.1037/h0077714
Note: See TracBrowser for help on using the repository browser.