Passed
Push — master ( 8f2b28...41c717 )
by Jacob
01:34
created

yui/src/recording/js/videomodule.js   B

Complexity

Total Complexity 41
Complexity/F 1.86

Size

Lines of Code 261
Function Count 22

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 21
Bugs 1 Features 0
Metric Value
cc 0
c 21
b 1
f 0
nc 12
dl 0
loc 261
rs 8.2769
wmc 41
mnd 5
bc 34
fnc 22
bpm 1.5454
cpm 1.8636
noi 19

3 Functions

Rating   Name   Duplication   Size   Complexity  
B M.atto_recordrtc.videomodule.capture_audio_video 0 26 1
B M.atto_recordrtc.videomodule.init 0 169 1
A M.atto_recordrtc.videomodule.stop_recording 0 56 1

How to fix   Complexity   

Complexity

Complex classes like yui/src/recording/js/videomodule.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// Atto recordrtc library functions.
2
// @package    atto_recordrtc.
3
// @author     Jesus Federico (jesus [at] blindsidenetworks [dt] com).
4
// @copyright  2016 to present, Blindside Networks Inc.
5
// @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
6
7
/*jshint es5: true */
8
/*jshint onevar: false */
9
/*jshint shadow: true */
10
/*global M */
11
/*global MediaRecorder */
12
/*global URL */
13
/*global InstallTrigger */
14
15
M.atto_recordrtc = M.atto_recordrtc || {};
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
16
17
// Shorten access to M.atto_recordrtc.commonmodule namespace.
18
var cm = M.atto_recordrtc.commonmodule;
19
20
M.atto_recordrtc.videomodule = {
21
    init: function(scope) {
22
        // Assignment of global variables.
23
        cm.editorScope = scope; // Allows access to the editor's "this" context.
24
        cm.alertWarning = Y.one('div#alert-warning');
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
25
        cm.alertDanger = Y.one('div#alert-danger');
26
        cm.player = Y.one('video#player');
27
        cm.playerDOM = document.querySelector('video#player');
28
        cm.startStopBtn = Y.one('button#start-stop');
29
        cm.uploadBtn = Y.one('button#upload');
30
        cm.recType = 'video';
31
        cm.olderMoodle = scope.get('oldermoodle');
32
        // Extract the numbers from the string, and convert to bytes.
33
        cm.maxUploadSize = parseInt(scope.get('maxrecsize').match(/\d+/)[0], 10) * Math.pow(1024, 2);
34
35
        // Show alert and redirect user if connection is not secure.
36
        cm.check_secure();
37
        // Show alert if using non-ideal browser.
38
        cm.check_browser();
39
40
        // Run when user clicks on "record" button.
41
        cm.startStopBtn.on('click', function() {
42
            cm.startStopBtn.set('disabled', true);
43
44
            // If button is displaying "Start Recording" or "Record Again".
45
            if ((cm.startStopBtn.get('textContent') === M.util.get_string('startrecording', 'atto_recordrtc')) ||
46
                (cm.startStopBtn.get('textContent') === M.util.get_string('recordagain', 'atto_recordrtc')) ||
47
                (cm.startStopBtn.get('textContent') === M.util.get_string('recordingfailed', 'atto_recordrtc'))) {
48
                // Make sure the upload button is not shown.
49
                cm.uploadBtn.ancestor().ancestor().addClass('hide');
50
51
                // Change look of recording button.
52
                if (!cm.olderMoodle) {
53
                    cm.startStopBtn.replaceClass('btn-outline-danger', 'btn-danger');
54
                }
55
56
                // Empty the array containing the previously recorded chunks.
57
                cm.chunks = [];
58
                cm.blobSize = 0;
59
60
                // Initialize common configurations.
61
                var commonConfig = {
62
                    // When the stream is captured from the microphone/webcam.
63
                    onMediaCaptured: function(stream) {
64
                        // Make video stream available at a higher level by making it a property of the common module.
65
                        cm.stream = stream;
66
67
                        if (cm.startStopBtn.mediaCapturedCallback) {
68
                            cm.startStopBtn.mediaCapturedCallback();
69
                        }
70
                    },
71
72
                    // Revert button to "Record Again" when recording is stopped.
73
                    onMediaStopped: function(btnLabel) {
74
                        cm.startStopBtn.set('textContent', btnLabel);
75
                    },
76
77
                    // Handle recording errors.
78
                    onMediaCapturingFailed: function(error) {
79
                        var btnLabel = null;
80
81
                        // Handle getUserMedia-thrown errors.
82
                        switch (error.name) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
83
                            case 'AbortError':
84
                                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
85
                                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
86
                                        title: M.util.get_string('gumabort_title', 'atto_recordrtc'),
87
                                        message: M.util.get_string('gumabort', 'atto_recordrtc')
88
                                    });
89
                                });
90
91
                                btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc');
92
                                break;
93
                            case 'NotAllowedError':
94
                                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
95
                                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
96
                                        title: M.util.get_string('gumnotallowed_title', 'atto_recordrtc'),
97
                                        message: M.util.get_string('gumnotallowed', 'atto_recordrtc')
98
                                    });
99
                                });
100
101
                                btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc');
102
                                break;
103
                            case 'NotFoundError':
104
                                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
105
                                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
106
                                        title: M.util.get_string('gumnotfound_title', 'atto_recordrtc'),
107
                                        message: M.util.get_string('gumnotfound', 'atto_recordrtc')
108
                                    });
109
                                });
110
111
                                btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc');
112
                                break;
113
                            case 'NotReadableError':
114
                                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
115
                                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
116
                                        title: M.util.get_string('gumnotreadable_title', 'atto_recordrtc'),
117
                                        message: M.util.get_string('gumnotreadable', 'atto_recordrtc')
118
                                    });
119
                                });
120
121
                                btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc');
122
                                break;
123
                            case 'OverConstrainedError':
124
                                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
125
                                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
126
                                        title: M.util.get_string('gumoverconstrained_title', 'atto_recordrtc'),
127
                                        message: M.util.get_string('gumoverconstrained', 'atto_recordrtc')
128
                                    });
129
                                });
130
131
                                btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc');
132
                                break;
133
                            case 'SecurityError':
134
                                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
135
                                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
136
                                        title: M.util.get_string('gumsecurity_title', 'atto_recordrtc'),
137
                                        message: M.util.get_string('gumsecurity', 'atto_recordrtc')
138
                                    });
139
                                });
140
141
                                cm.editorScope.closeDialogue(cm.editorScope);
142
                                break;
143
                            case 'TypeError':
144
                                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
145
                                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
146
                                        title: M.util.get_string('gumtype_title', 'atto_recordrtc'),
147
                                        message: M.util.get_string('gumtype', 'atto_recordrtc')
148
                                    });
149
                                });
150
151
                                btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc');
152
                        }
153
154
                        // Proceed to treat as a stopped recording.
155
                        commonConfig.onMediaStopped(btnLabel);
156
                    }
157
                };
158
159
                // Show video tag without controls to view webcam stream.
160
                cm.player.ancestor().ancestor().removeClass('hide');
161
                cm.player.set('controls', false);
162
163
                // Capture audio+video stream from webcam/microphone.
164
                M.atto_recordrtc.videomodule.capture_audio_video(commonConfig);
165
166
                // When audio+video stream is successfully captured, start recording.
167
                cm.startStopBtn.mediaCapturedCallback = function() {
168
                    cm.start_recording(cm.recType, cm.stream);
169
                };
170
            } else { // If button is displaying "Stop Recording".
171
                // First of all clears the countdownTicker.
172
                clearInterval(cm.countdownTicker);
173
174
                // Disable "Record Again" button for 1s to allow background processing (closing streams).
175
                setTimeout(function() {
176
                    cm.startStopBtn.set('disabled', false);
177
                }, 1000);
178
179
                // Stop recording.
180
                M.atto_recordrtc.videomodule.stop_recording(cm.stream);
181
182
                // Change button to offer to record again.
183
                cm.startStopBtn.set('textContent', M.util.get_string('recordagain', 'atto_recordrtc'));
184
                if (!cm.olderMoodle) {
185
                    cm.startStopBtn.replaceClass('btn-danger', 'btn-outline-danger');
186
                }
187
            }
188
        });
189
    },
190
191
    // Setup to get audio+video stream from microphone/webcam.
192
    capture_audio_video: function(config) {
193
        cm.capture_user_media(
194
            // Media constraints.
195
            {
196
                audio: true,
197
                video: {
198
                    width: {ideal: 640},
199
                    height: {ideal: 480}
200
                }
201
            },
202
203
            // Success callback.
204
            function(audioVideoStream) {
205
                // Set video player source to microphone+webcam stream, and play it back as it's recording.
206
                cm.playerDOM.srcObject = audioVideoStream;
207
                cm.playerDOM.play();
208
209
                config.onMediaCaptured(audioVideoStream);
210
            },
211
212
            // Error callback.
213
            function(error) {
214
                config.onMediaCapturingFailed(error);
215
            }
216
        );
217
    },
218
219
    stop_recording: function(stream) {
220
        // Stop recording microphone stream.
221
        cm.mediaRecorder.stop();
222
223
        // Stop each individual MediaTrack.
224
        stream.getTracks().forEach(function(track) {
225
            track.stop();
226
        });
227
228
        // Set source of video player.
229
        var blob = new Blob(cm.chunks, {type: cm.mediaRecorder.mimeType});
0 ignored issues
show
Bug introduced by
The variable Blob seems to be never declared. If this is a global, consider adding a /** global: Blob */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
230
        cm.player.set('src', URL.createObjectURL(blob));
231
232
        // Enable controls for video player, and unmute.
233
        cm.player.set('muted', false);
234
        cm.player.set('controls', true);
235
236
        // Show upload button.
237
        cm.uploadBtn.ancestor().ancestor().removeClass('hide');
238
        cm.uploadBtn.set('textContent', M.util.get_string('attachrecording', 'atto_recordrtc'));
239
        cm.uploadBtn.set('disabled', false);
240
241
        // Handle when upload button is clicked.
242
        cm.uploadBtn.on('click', function() {
243
            // Trigger error if no recording has been made.
244
            if (!cm.player.getAttribute('src') || cm.chunks === []) {
245
                Y.use('moodle-core-notification-alert', function() {
0 ignored issues
show
Bug introduced by
The variable Y seems to be never declared. If this is a global, consider adding a /** global: Y */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
246
                    new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...ode(atto_recordrtc)))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
247
                        title: M.util.get_string('norecordingfound_title', 'atto_recordrtc'),
248
                        message: M.util.get_string('norecordingfound', 'atto_recordrtc')
249
                    });
250
                });
251
            } else {
252
                cm.uploadBtn.set('disabled', true);
253
254
                // Upload recording to server.
255
                cm.upload_to_server(cm.recType, function(progress, fileURLOrError) {
256
                    if (progress === 'ended') { // Insert annotation in text.
257
                        cm.uploadBtn.set('disabled', false);
258
                        cm.insert_annotation(cm.recType, fileURLOrError);
259
                    } else if (progress === 'upload-failed') { // Show error message in upload button.
260
                        cm.uploadBtn.set('disabled', false);
261
                        cm.uploadBtn.set('textContent', M.util.get_string('uploadfailed', 'atto_recordrtc') + ' ' + fileURLOrError);
262
                    } else if (progress === 'upload-failed-404') { // 404 error = File too large in Moodle.
263
                        cm.uploadBtn.set('disabled', false);
264
                        cm.uploadBtn.set('textContent', M.util.get_string('uploadfailed404', 'atto_recordrtc'));
265
                    } else if (progress === 'upload-aborted') {
266
                        cm.uploadBtn.set('disabled', false);
267
                        cm.uploadBtn.set('textContent', M.util.get_string('uploadaborted', 'atto_recordrtc') + ' ' + fileURLOrError);
268
                    } else {
269
                        cm.uploadBtn.set('textContent', progress);
270
                    }
271
                });
272
            }
273
        });
274
    }
275
};
276