MDL-60848 atto_recordrtc: language string changes
[moodle.git] / lib / editor / atto / plugins / recordrtc / yui / build / moodle-atto_recordrtc-recording / moodle-atto_recordrtc-recording.js
CommitLineData
b4bbf31b 1YUI.add('moodle-atto_recordrtc-recording', function (Y, NAME) {
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17//
18
19/**
20 * Atto recordrtc library functions
21 *
22 * @package atto_recordrtc
23 * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
24 * @author Jacob Prud'homme (jacob [dt] prudhomme [at] blindsidenetworks [dt] com)
25 * @copyright 2017 Blindside Networks Inc.
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 */
28
29// ESLint directives.
30/* eslint-disable camelcase, no-alert, spaced-comment */
31
32// JSHint directives.
33/*global M */
34/*jshint es5: true */
35/*jshint onevar: false */
36/*jshint shadow: true */
37
38// Scrutinizer CI directives.
39/** global: M */
40/** global: Y */
41
42M.atto_recordrtc = M.atto_recordrtc || {};
43
44// Shorten access to M.atto_recordrtc.commonmodule namespace.
45var cm = M.atto_recordrtc.commonmodule,
46 am = M.atto_recordrtc.abstractmodule;
47
48M.atto_recordrtc.commonmodule = {
49 // Unitialized variables to be used by the other modules.
50 editorScope: null,
51 alertWarning: null,
52 alertDanger: null,
53 player: null,
54 playerDOM: null, // Used to manipulate DOM directly.
55 startStopBtn: null,
56 uploadBtn: null,
57 countdownSeconds: null,
58 countdownTicker: null,
59 recType: null,
60 stream: null,
61 mediaRecorder: null,
62 chunks: null,
63 blobSize: null,
b4bbf31b 64 maxUploadSize: null,
65
66 // Capture webcam/microphone stream.
67 capture_user_media: function(mediaConstraints, successCallback, errorCallback) {
68 window.navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);
69 },
70
71 // Add chunks of audio/video to array when made available.
72 handle_data_available: function(event) {
73 // Push recording slice to array.
74 cm.chunks.push(event.data);
75 // Size of all recorded data so far.
76 cm.blobSize += event.data.size;
77
78 // If total size of recording so far exceeds max upload limit, stop recording.
79 // An extra condition exists to avoid displaying alert twice.
80 if (cm.blobSize >= cm.maxUploadSize) {
81 if (!window.localStorage.getItem('alerted')) {
82 window.localStorage.setItem('alerted', 'true');
83
84 cm.startStopBtn.simulate('click');
85 am.show_alert('nearingmaxsize');
86 } else {
87 window.localStorage.removeItem('alerted');
88 }
89
90 cm.chunks.pop();
91 }
92 },
93
94 // Handle recording end.
95 handle_stop: function() {
96 // Set source of audio player.
97 var blob = new window.Blob(cm.chunks, {type: cm.mediaRecorder.mimeType});
98 cm.player.set('src', window.URL.createObjectURL(blob));
99
100 // Show audio player with controls enabled, and unmute.
101 cm.player.set('muted', false);
102 cm.player.set('controls', true);
103 cm.player.ancestor().ancestor().removeClass('hide');
104
105 // Show upload button.
106 cm.uploadBtn.ancestor().ancestor().removeClass('hide');
107 cm.uploadBtn.set('textContent', M.util.get_string('attachrecording', 'atto_recordrtc'));
108 cm.uploadBtn.set('disabled', false);
109
110 // Get dialogue centered.
111 cm.editorScope.getDialogue().centered();
112
113 // Handle when upload button is clicked.
114 cm.uploadBtn.on('click', function() {
115 // Trigger error if no recording has been made.
116 if (cm.chunks.length === 0) {
117 am.show_alert('norecordingfound');
118 } else {
119 cm.uploadBtn.set('disabled', true);
120
121 // Upload recording to server.
122 cm.upload_to_server(cm.recType, function(progress, fileURLOrError) {
123 if (progress === 'ended') { // Insert annotation in text.
124 cm.uploadBtn.set('disabled', false);
125 cm.insert_annotation(cm.recType, fileURLOrError);
126 } else if (progress === 'upload-failed') { // Show error message in upload button.
127 cm.uploadBtn.set('disabled', false);
128 cm.uploadBtn.set('textContent',
129 M.util.get_string('uploadfailed', 'atto_recordrtc') + ' ' + fileURLOrError);
130 } else if (progress === 'upload-failed-404') { // 404 error = File too large in Moodle.
131 cm.uploadBtn.set('disabled', false);
132 cm.uploadBtn.set('textContent', M.util.get_string('uploadfailed404', 'atto_recordrtc'));
133 } else if (progress === 'upload-aborted') {
134 cm.uploadBtn.set('disabled', false);
135 cm.uploadBtn.set('textContent',
136 M.util.get_string('uploadaborted', 'atto_recordrtc') + ' ' + fileURLOrError);
137 } else {
138 cm.uploadBtn.set('textContent', progress);
139 }
140 });
141 }
142 });
143 },
144
145 // Get everything set up to start recording.
146 start_recording: function(type, stream) {
147 // The options for the recording codecs and bitrates.
148 var options = am.select_rec_options(type);
149 cm.mediaRecorder = new window.MediaRecorder(stream, options);
150
151 // Initialize MediaRecorder events and start recording.
152 cm.mediaRecorder.ondataavailable = cm.handle_data_available;
153 cm.mediaRecorder.onstop = cm.handle_stop;
154 cm.mediaRecorder.start(1000); // Capture in 1s chunks. Must be set to work with Firefox.
155
156 // Mute audio, distracting while recording.
157 cm.player.set('muted', true);
158
159 // Set recording timer to the time specified in the settings.
160 cm.countdownSeconds = cm.editorScope.get('timelimit');
161 cm.countdownSeconds++;
162 var timerText = M.util.get_string('stoprecording', 'atto_recordrtc');
163 timerText += ' (<span id="minutes"></span>:<span id="seconds"></span>)';
164 cm.startStopBtn.setHTML(timerText);
165 cm.set_time();
166 cm.countdownTicker = window.setInterval(cm.set_time, 1000);
167
168 // Make button clickable again, to allow stopping recording.
169 cm.startStopBtn.set('disabled', false);
170 },
171
172 // Get everything set up to stop recording.
173 stop_recording: function(stream) {
174 // Stop recording stream.
175 cm.mediaRecorder.stop();
176
177 // Stop each individual MediaTrack.
178 var tracks = stream.getTracks();
179 for (var i = 0; i < tracks.length; i++) {
180 tracks[i].stop();
181 }
182 },
183
184 // Upload recorded audio/video to server.
185 upload_to_server: function(type, callback) {
186 var xhr = new window.XMLHttpRequest();
187
188 // Get src media of audio/video tag.
189 xhr.open('GET', cm.player.get('src'), true);
190 xhr.responseType = 'blob';
191
192 xhr.onload = function() {
193 if (xhr.status === 200) { // If src media was successfully retrieved.
194 // blob is now the media that the audio/video tag's src pointed to.
195 var blob = this.response;
196
197 // Generate filename with random ID and file extension.
198 var fileName = (Math.random() * 1000).toString().replace('.', '');
199 fileName += (type === 'audio') ? '-audio.ogg'
200 : '-video.webm';
201
202 // Create FormData to send to PHP filepicker-upload script.
203 var formData = new window.FormData(),
204 filepickerOptions = cm.editorScope.get('host').get('filepickeroptions').link,
205 repositoryKeys = window.Object.keys(filepickerOptions.repositories);
206
207 formData.append('repo_upload_file', blob, fileName);
208 formData.append('itemid', filepickerOptions.itemid);
209
210 for (var i = 0; i < repositoryKeys.length; i++) {
211 if (filepickerOptions.repositories[repositoryKeys[i]].type === 'upload') {
212 formData.append('repo_id', filepickerOptions.repositories[repositoryKeys[i]].id);
213 break;
214 }
215 }
216
217 formData.append('env', filepickerOptions.env);
218 formData.append('sesskey', M.cfg.sesskey);
219 formData.append('client_id', filepickerOptions.client_id);
220 formData.append('savepath', '/');
221 formData.append('ctx_id', filepickerOptions.context.id);
222
223 // Pass FormData to PHP script using XHR.
224 var uploadEndpoint = M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload';
225 cm.make_xmlhttprequest(uploadEndpoint, formData,
226 function(progress, responseText) {
227 if (progress === 'upload-ended') {
228 callback('ended', window.JSON.parse(responseText).url);
229 } else {
230 callback(progress);
231 }
232 }
233 );
234 }
235 };
236
237 xhr.send();
238 },
239
240 // Handle XHR sending/receiving/status.
241 make_xmlhttprequest: function(url, data, callback) {
242 var xhr = new window.XMLHttpRequest();
243
244 xhr.onreadystatechange = function() {
245 if ((xhr.readyState === 4) && (xhr.status === 200)) { // When request is finished and successful.
246 callback('upload-ended', xhr.responseText);
247 } else if (xhr.status === 404) { // When request returns 404 Not Found.
248 callback('upload-failed-404');
249 }
250 };
251
252 xhr.upload.onprogress = function(event) {
253 callback(Math.round(event.loaded / event.total * 100) + "% " + M.util.get_string('uploadprogress', 'atto_recordrtc'));
254 };
255
256 xhr.upload.onerror = function(error) {
257 callback('upload-failed', error);
258 };
259
260 xhr.upload.onabort = function(error) {
261 callback('upload-aborted', error);
262 };
263
264 // POST FormData to PHP script that handles uploading/saving.
265 xhr.open('POST', url);
266 xhr.send(data);
267 },
268
269 // Makes 1min and 2s display as 1:02 on timer instead of 1:2, for example.
270 pad: function(val) {
271 var valString = val + "";
272
273 if (valString.length < 2) {
274 return "0" + valString;
275 } else {
276 return valString;
277 }
278 },
279
280 // Functionality to make recording timer count down.
281 // Also makes recording stop when time limit is hit.
282 set_time: function() {
283 cm.countdownSeconds--;
284
285 cm.startStopBtn.one('span#seconds').set('textContent', cm.pad(cm.countdownSeconds % 60));
286 cm.startStopBtn.one('span#minutes').set('textContent', cm.pad(window.parseInt(cm.countdownSeconds / 60, 10)));
287
288 if (cm.countdownSeconds === 0) {
289 cm.startStopBtn.simulate('click');
290 }
291 },
292
293 // Generates link to recorded annotation to be inserted.
294 create_annotation: function(type, recording_url) {
295 var linkText = window.prompt(M.util.get_string('annotationprompt', 'atto_recordrtc'),
296 M.util.get_string('annotation:' + type, 'atto_recordrtc'));
297
298 // Return HTML for annotation link, if user did not press "Cancel".
299 if (!linkText) {
300 return undefined;
301 } else {
302 var annotation = '<a target="_blank" href="' + recording_url + '">' + linkText + '</a>';
303 return annotation;
304 }
305 },
306
307 // Inserts link to annotation in editor text area.
308 insert_annotation: function(type, recording_url) {
309 var annotation = cm.create_annotation(type, recording_url);
310
311 // Insert annotation link.
312 // If user pressed "Cancel", just go back to main recording screen.
313 if (!annotation) {
314 cm.uploadBtn.set('textContent', M.util.get_string('attachrecording', 'atto_recordrtc'));
315 } else {
316 cm.editorScope.setLink(cm.editorScope, annotation);
317 }
318 }
319};
320// This file is part of Moodle - http://moodle.org/
321//
322// Moodle is free software: you can redistribute it and/or modify
323// it under the terms of the GNU General Public License as published by
324// the Free Software Foundation, either version 3 of the License, or
325// (at your option) any later version.
326//
327// Moodle is distributed in the hope that it will be useful,
328// but WITHOUT ANY WARRANTY; without even the implied warranty of
329// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
330// GNU General Public License for more details.
331//
332// You should have received a copy of the GNU General Public License
333// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
334//
335
336/**
337 * Atto recordrtc library functions for checking browser compatibility
338 *
339 * @package atto_recordrtc
340 * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
341 * @author Jacob Prud'homme (jacob [dt] prudhomme [at] blindsidenetworks [dt] com)
342 * @copyright 2017 Blindside Networks Inc.
343 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
344 */
345
346// ESLint directives.
347/* eslint-disable camelcase */
348
349// Scrutinizer CI directives.
350/** global: M */
351
352M.atto_recordrtc = M.atto_recordrtc || {};
353
354// Shorten access to module namespaces.
355var cm = M.atto_recordrtc.commonmodule,
356 am = M.atto_recordrtc.abstractmodule;
357
358M.atto_recordrtc.compatcheckmodule = {
359 // Show alert and close plugin if browser does not support WebRTC at all.
360 check_has_gum: function() {
361 if (!(navigator.mediaDevices && window.MediaRecorder)) {
362 am.show_alert('nowebrtc', function() {
363 cm.editorScope.closeDialogue(cm.editorScope);
364 });
365 }
366 },
367
368 // Notify and redirect user if plugin is used from insecure location.
369 check_secure: function() {
370 var isSecureOrigin = (window.location.protocol === 'https:') ||
371 (window.location.host.indexOf('localhost') !== -1);
372
373 if (!isSecureOrigin) {
374 cm.alertDanger.ancestor().ancestor().removeClass('hide');
b4bbf31b 375 }
376 },
b4bbf31b 377};
378// This file is part of Moodle - http://moodle.org/
379//
380// Moodle is free software: you can redistribute it and/or modify
381// it under the terms of the GNU General Public License as published by
382// the Free Software Foundation, either version 3 of the License, or
383// (at your option) any later version.
384//
385// Moodle is distributed in the hope that it will be useful,
386// but WITHOUT ANY WARRANTY; without even the implied warranty of
387// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
388// GNU General Public License for more details.
389//
390// You should have received a copy of the GNU General Public License
391// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
392//
393
394/**
395 * Atto recordrtc library functions for function abstractions
396 *
397 * @package atto_recordrtc
398 * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
399 * @author Jacob Prud'homme (jacob [dt] prudhomme [at] blindsidenetworks [dt] com)
400 * @copyright 2017 Blindside Networks Inc.
401 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
402 */
403
404// ESLint directives.
405/* eslint-disable camelcase */
406
407// Scrutinizer CI directives.
408/** global: M */
409/** global: Y */
410
411M.atto_recordrtc = M.atto_recordrtc || {};
412
413// Shorten access to module namespaces.
414var cm = M.atto_recordrtc.commonmodule,
415 am = M.atto_recordrtc.abstractmodule;
416
417M.atto_recordrtc.abstractmodule = {
418 // A helper for making a Moodle alert appear.
419 // Subject is the content of the alert (which error ther alert is for).
420 // Possibility to add on-alert-close event.
421 show_alert: function(subject, onCloseEvent) {
422 Y.use('moodle-core-notification-alert', function() {
423 var dialogue = new M.core.alert({
424 title: M.util.get_string(subject + '_title', 'atto_recordrtc'),
425 message: M.util.get_string(subject, 'atto_recordrtc')
426 });
427
428 if (onCloseEvent) {
429 dialogue.after('complete', onCloseEvent);
430 }
431 });
432 },
433
434 // Handle getUserMedia errors.
435 handle_gum_errors: function(error, commonConfig) {
436 var btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc'),
437 treatAsStopped = function() {
438 commonConfig.onMediaStopped(btnLabel);
439 };
440
441 // Changes 'CertainError' -> 'gumcertain' to match language string names.
442 var stringName = 'gum' + error.name.replace('Error', '').toLowerCase();
443
444 // After alert, proceed to treat as stopped recording, or close dialogue.
445 if (stringName !== 'gumsecurity') {
446 am.show_alert(stringName, treatAsStopped);
447 } else {
448 am.show_alert(stringName, function() {
449 cm.editorScope.closeDialogue(cm.editorScope);
450 });
451 }
452 },
453
454 // Select best options for the recording codec.
455 select_rec_options: function(recType) {
456 var types, options;
457
458 if (recType === 'audio') {
459 types = [
460 'audio/webm;codecs=opus',
461 'audio/ogg;codecs=opus'
462 ];
463 options = {
464 audioBitsPerSecond: window.parseInt(cm.editorScope.get('audiobitrate'))
465 };
466 } else {
467 types = [
468 'video/webm;codecs=vp9,opus',
469 'video/webm;codecs=h264,opus',
470 'video/webm;codecs=vp8,opus'
471 ];
472 options = {
473 audioBitsPerSecond: window.parseInt(cm.editorScope.get('audiobitrate')),
474 videoBitsPerSecond: window.parseInt(cm.editorScope.get('videobitrate'))
475 };
476 }
477
478 var compatTypes = types.filter(function(type) {
479 return window.MediaRecorder.isTypeSupported(type);
480 });
481
482 if (compatTypes.length !== 0) {
483 options.mimeType = compatTypes[0];
484 }
485
486 return options;
487 }
488};
489// This file is part of Moodle - http://moodle.org/
490//
491// Moodle is free software: you can redistribute it and/or modify
492// it under the terms of the GNU General Public License as published by
493// the Free Software Foundation, either version 3 of the License, or
494// (at your option) any later version.
495//
496// Moodle is distributed in the hope that it will be useful,
497// but WITHOUT ANY WARRANTY; without even the implied warranty of
498// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
499// GNU General Public License for more details.
500//
501// You should have received a copy of the GNU General Public License
502// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
503//
504
505/**
506 * Atto recordrtc library functions
507 *
508 * @package atto_recordrtc
509 * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
510 * @author Jacob Prud'homme (jacob [dt] prudhomme [at] blindsidenetworks [dt] com)
511 * @copyright 2017 Blindside Networks Inc.
512 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
513 */
514
515// ESLint directives.
516/* eslint-disable camelcase, spaced-comment */
517
518// Scrutinizer CI directives.
519/** global: M */
520/** global: Y */
521
522M.atto_recordrtc = M.atto_recordrtc || {};
523
524// Shorten access to module namespaces.
525var cm = M.atto_recordrtc.commonmodule,
526 am = M.atto_recordrtc.abstractmodule,
527 ccm = M.atto_recordrtc.compatcheckmodule;
528
529M.atto_recordrtc.audiomodule = {
530 init: function(scope) {
531 // Assignment of global variables.
532 cm.editorScope = scope; // Allows access to the editor's "this" context.
533 cm.alertWarning = Y.one('div#alert-warning');
534 cm.alertDanger = Y.one('div#alert-danger');
535 cm.player = Y.one('audio#player');
536 cm.playerDOM = document.querySelector('audio#player');
537 cm.startStopBtn = Y.one('button#start-stop');
538 cm.uploadBtn = Y.one('button#upload');
539 cm.recType = 'audio';
b4bbf31b 540 // Extract the numbers from the string, and convert to bytes.
541 cm.maxUploadSize = window.parseInt(scope.get('maxrecsize').match(/\d+/)[0], 10) * Math.pow(1024, 2);
542
543 // Show alert and close plugin if WebRTC is not supported.
544 ccm.check_has_gum();
545 // Show alert and redirect user if connection is not secure.
546 ccm.check_secure();
b4bbf31b 547
548 // Run when user clicks on "record" button.
549 cm.startStopBtn.on('click', function() {
550 cm.startStopBtn.set('disabled', true);
551
552 // If button is displaying "Start Recording" or "Record Again".
553 if ((cm.startStopBtn.get('textContent') === M.util.get_string('startrecording', 'atto_recordrtc')) ||
554 (cm.startStopBtn.get('textContent') === M.util.get_string('recordagain', 'atto_recordrtc')) ||
555 (cm.startStopBtn.get('textContent') === M.util.get_string('recordingfailed', 'atto_recordrtc'))) {
556 // Make sure the audio player and upload button are not shown.
557 cm.player.ancestor().ancestor().addClass('hide');
558 cm.uploadBtn.ancestor().ancestor().addClass('hide');
559
560 // Change look of recording button.
0174e72b 561 cm.startStopBtn.replaceClass('btn-outline-danger', 'btn-danger');
b4bbf31b 562
563 // Empty the array containing the previously recorded chunks.
564 cm.chunks = [];
565 cm.blobSize = 0;
566
567 // Initialize common configurations.
568 var commonConfig = {
569 // When the stream is captured from the microphone/webcam.
570 onMediaCaptured: function(stream) {
571 // Make audio stream available at a higher level by making it a property of the common module.
572 cm.stream = stream;
573
574 cm.start_recording(cm.recType, cm.stream);
575 },
576
577 // Revert button to "Record Again" when recording is stopped.
578 onMediaStopped: function(btnLabel) {
579 cm.startStopBtn.set('textContent', btnLabel);
580 cm.startStopBtn.set('disabled', false);
0174e72b 581 cm.startStopBtn.replaceClass('btn-danger', 'btn-outline-danger');
b4bbf31b 582 },
583
584 // Handle recording errors.
585 onMediaCapturingFailed: function(error) {
586 am.handle_gum_errors(error, commonConfig);
587 }
588 };
589
590 // Capture audio stream from microphone.
591 M.atto_recordrtc.audiomodule.capture_audio(commonConfig);
592 } else { // If button is displaying "Stop Recording".
593 // First of all clears the countdownTicker.
594 window.clearInterval(cm.countdownTicker);
595
596 // Disable "Record Again" button for 1s to allow background processing (closing streams).
597 window.setTimeout(function() {
598 cm.startStopBtn.set('disabled', false);
599 }, 1000);
600
601 // Stop recording.
602 cm.stop_recording(cm.stream);
603
604 // Change button to offer to record again.
605 cm.startStopBtn.set('textContent', M.util.get_string('recordagain', 'atto_recordrtc'));
0174e72b 606 cm.startStopBtn.replaceClass('btn-danger', 'btn-outline-danger');
b4bbf31b 607 }
608
609 // Get dialogue centered.
610 cm.editorScope.getDialogue().centered();
611 });
612 },
613
614 // Setup to get audio stream from microphone.
615 capture_audio: function(config) {
616 cm.capture_user_media(
617 // Media constraints.
618 {
619 audio: true
620 },
621
622 // Success callback.
623 function(audioStream) {
624 // Set audio player source to microphone stream.
625 cm.playerDOM.srcObject = audioStream;
626
627 config.onMediaCaptured(audioStream);
628 },
629
630 // Error callback.
631 function(error) {
632 config.onMediaCapturingFailed(error);
633 }
634 );
635 }
636};
637// This file is part of Moodle - http://moodle.org/
638//
639// Moodle is free software: you can redistribute it and/or modify
640// it under the terms of the GNU General Public License as published by
641// the Free Software Foundation, either version 3 of the License, or
642// (at your option) any later version.
643//
644// Moodle is distributed in the hope that it will be useful,
645// but WITHOUT ANY WARRANTY; without even the implied warranty of
646// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
647// GNU General Public License for more details.
648//
649// You should have received a copy of the GNU General Public License
650// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
651//
652
653/**
654 * Atto recordrtc library functions
655 *
656 * @package atto_recordrtc
657 * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
658 * @author Jacob Prud'homme (jacob [dt] prudhomme [at] blindsidenetworks [dt] com)
659 * @copyright 2017 Blindside Networks Inc.
660 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
661 */
662
663// ESLint directives.
664/* eslint-disable camelcase, spaced-comment */
665
666// Scrutinizer CI directives.
667/** global: M */
668/** global: Y */
669
670M.atto_recordrtc = M.atto_recordrtc || {};
671
672// Shorten access to module namespaces.
673var cm = M.atto_recordrtc.commonmodule,
674 am = M.atto_recordrtc.abstractmodule,
675 ccm = M.atto_recordrtc.compatcheckmodule;
676
677M.atto_recordrtc.videomodule = {
678 init: function(scope) {
679 // Assignment of global variables.
680 cm.editorScope = scope; // Allows access to the editor's "this" context.
681 cm.alertWarning = Y.one('div#alert-warning');
682 cm.alertDanger = Y.one('div#alert-danger');
683 cm.player = Y.one('video#player');
684 cm.playerDOM = document.querySelector('video#player');
685 cm.startStopBtn = Y.one('button#start-stop');
686 cm.uploadBtn = Y.one('button#upload');
687 cm.recType = 'video';
b4bbf31b 688 // Extract the numbers from the string, and convert to bytes.
689 cm.maxUploadSize = window.parseInt(scope.get('maxrecsize').match(/\d+/)[0], 10) * Math.pow(1024, 2);
690
691 // Show alert and close plugin if WebRTC is not supported.
692 ccm.check_has_gum();
693 // Show alert and redirect user if connection is not secure.
694 ccm.check_secure();
b4bbf31b 695
696 // Run when user clicks on "record" button.
697 cm.startStopBtn.on('click', function() {
698 cm.startStopBtn.set('disabled', true);
699
700 // If button is displaying "Start Recording" or "Record Again".
701 if ((cm.startStopBtn.get('textContent') === M.util.get_string('startrecording', 'atto_recordrtc')) ||
702 (cm.startStopBtn.get('textContent') === M.util.get_string('recordagain', 'atto_recordrtc')) ||
703 (cm.startStopBtn.get('textContent') === M.util.get_string('recordingfailed', 'atto_recordrtc'))) {
704 // Make sure the upload button is not shown.
705 cm.uploadBtn.ancestor().ancestor().addClass('hide');
706
707 // Change look of recording button.
0174e72b 708 cm.startStopBtn.replaceClass('btn-outline-danger', 'btn-danger');
b4bbf31b 709
710 // Empty the array containing the previously recorded chunks.
711 cm.chunks = [];
712 cm.blobSize = 0;
713
714 // Initialize common configurations.
715 var commonConfig = {
716 // When the stream is captured from the microphone/webcam.
717 onMediaCaptured: function(stream) {
718 // Make video stream available at a higher level by making it a property of the common module.
719 cm.stream = stream;
720
721 cm.start_recording(cm.recType, cm.stream);
722 },
723
724 // Revert button to "Record Again" when recording is stopped.
725 onMediaStopped: function(btnLabel) {
726 cm.startStopBtn.set('textContent', btnLabel);
727 cm.startStopBtn.set('disabled', false);
0174e72b 728 cm.startStopBtn.replaceClass('btn-danger', 'btn-outline-danger');
b4bbf31b 729 },
730
731 // Handle recording errors.
732 onMediaCapturingFailed: function(error) {
733 am.handle_gum_errors(error, commonConfig);
734 }
735 };
736
737 // Show video tag without controls to view webcam stream.
738 cm.player.ancestor().ancestor().removeClass('hide');
739 cm.player.set('controls', false);
740
741 // Capture audio+video stream from webcam/microphone.
742 M.atto_recordrtc.videomodule.capture_audio_video(commonConfig);
743 } else { // If button is displaying "Stop Recording".
744 // First of all clears the countdownTicker.
745 window.clearInterval(cm.countdownTicker);
746
747 // Disable "Record Again" button for 1s to allow background processing (closing streams).
748 window.setTimeout(function() {
749 cm.startStopBtn.set('disabled', false);
750 }, 1000);
751
752 // Stop recording.
753 cm.stop_recording(cm.stream);
754
755 // Change button to offer to record again.
756 cm.startStopBtn.set('textContent', M.util.get_string('recordagain', 'atto_recordrtc'));
0174e72b 757 cm.startStopBtn.replaceClass('btn-danger', 'btn-outline-danger');
b4bbf31b 758 }
759
760 // Get dialogue centered.
761 cm.editorScope.getDialogue().centered();
762 });
763 },
764
765 // Setup to get audio+video stream from microphone/webcam.
766 capture_audio_video: function(config) {
767 cm.capture_user_media(
768 // Media constraints.
769 {
770 audio: true,
771 video: {
772 width: {ideal: 640},
773 height: {ideal: 480}
774 }
775 },
776
777 // Success callback.
778 function(audioVideoStream) {
779 // Set video player source to microphone+webcam stream, and play it back as it's recording.
780 cm.playerDOM.srcObject = audioVideoStream;
781 cm.playerDOM.play();
782
783 config.onMediaCaptured(audioVideoStream);
784 },
785
786 // Error callback.
787 function(error) {
788 config.onMediaCapturingFailed(error);
789 }
790 );
791 }
792};
793
794
795}, '@VERSION@', {"requires": ["moodle-atto_recordrtc-button"]});