Completed
Push — master ( aa935e...acd3c7 )
by Jacob
01:24
created

tinymce/js/videomodule.js   B

Complexity

Total Complexity 41
Complexity/F 1.86

Size

Lines of Code 255
Function Count 22

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 25
Bugs 4 Features 1
Metric Value
wmc 41
dl 0
loc 255
rs 8.2769
c 25
b 4
f 1
cc 0
nc 6
mnd 5
bc 34
fnc 22
bpm 1.5454
cpm 1.8636
noi 20

2 Functions

Rating   Name   Duplication   Size   Complexity  
B M.tinymce_recordrtc.capture_audio_video 0 26 1
A M.tinymce_recordrtc.stop_recording_video 0 57 1

How to fix   Complexity   

Complexity

Complex classes like tinymce/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
// TinyMCE recordrtc library functions.
2
// @package    tinymce_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
/** global: M */
8
/** global: bowser */
9
/** global: recordrtc */
10
/** global: Blob */
11
/** global: player */
12
/** global: startStopBtn */
13
/** global: uploadBtn */
14
/** global: countdownSeconds */
15
/** global: countdownTicker */
16
/** global: recType */
17
/** global: mediaRecorder */
18
/** global: chunks */
19
/** global: blobSize */
20
/** global: maxUploadSize */
21
22
/**
23
 * This function is initialized from PHP
24
 *
25
 * @param {Object}
26
 *            Y YUI instance
27
 */
28
M.tinymce_recordrtc.view_init = function() {
29
    // Assignment of global variables.
30
    alertWarning = document.querySelector('div#alert-warning');
0 ignored issues
show
Bug introduced by
The variable alertWarning seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.alertWarning.
Loading history...
31
    alertDanger = document.querySelector('div#alert-danger');
0 ignored issues
show
Bug introduced by
The variable alertDanger seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.alertDanger.
Loading history...
32
    player = document.querySelector('video#player');
33
    startStopBtn = document.querySelector('button#start-stop');
34
    uploadBtn = document.querySelector('button#upload');
35
    recType = 'video';
36
    // Extract the numbers from the string, and convert to bytes.
37
    maxUploadSize = parseInt(recordrtc.maxfilesize.match(/\d+/)[0], 10) * Math.pow(1024, 2);
38
39
    // Show alert and redirect user if connection is not secure.
40
    M.tinymce_recordrtc.check_secure();
41
    // Show alert if using non-ideal browser.
42
    M.tinymce_recordrtc.check_browser();
43
44
    // Run when user clicks on "record" button.
45
    startStopBtn.onclick = function() {
46
        var btn = this;
47
        btn.disabled = true;
48
49
        // If button is displaying "Start Recording" or "Record Again".
50
        if ((btn.textContent === M.util.get_string('startrecording', 'tinymce_recordrtc')) ||
51
            (btn.textContent === M.util.get_string('recordagain', 'tinymce_recordrtc')) ||
52
            (btn.textContent === M.util.get_string('recordingfailed', 'tinymce_recordrtc'))) {
53
            // Make sure the upload button is not shown.
54
            uploadBtn.parentElement.parentElement.classList.add('hide');
55
56
            // Change look of recording button.
57
            if (!recordrtc.oldermoodle) {
58
                startStopBtn.classList.remove('btn-outline-danger');
59
                startStopBtn.classList.add('btn-danger');
60
            }
61
62
            // Empty the array containing the previously recorded chunks.
63
            chunks = [];
64
            blobSize = 0;
65
66
            // Initialize common configurations.
67
            var commonConfig = {
68
                // When the stream is captured from the microphone/webcam.
69
                onMediaCaptured: function(stream) {
70
                    // Make video stream available at a higher level by making it a property of btn.
71
                    btn.stream = stream;
72
73
                    if (btn.mediaCapturedCallback) {
74
                        btn.mediaCapturedCallback();
75
                    }
76
                },
77
78
                // Revert button to "Record Again" when recording is stopped.
79
                onMediaStopped: function(btnLabel) {
80
                    btn.textContent = btnLabel;
81
                },
82
83
                // Handle recording errors.
84
                onMediaCapturingFailed: function(error) {
85
                    var btnLabel = null;
86
87
                    // Handle getUserMedia-thrown errors.
88
                    switch (error.name) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
89
                        case 'AbortError':
90
                            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...
91
                                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
92
                                    title: M.util.get_string('gumabort_title', 'tinymce_recordrtc'),
93
                                    message: M.util.get_string('gumabort', 'tinymce_recordrtc')
94
                                });
95
                            });
96
97
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
98
                            break;
99
                        case 'NotAllowedError':
100
                            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...
101
                                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
102
                                    title: M.util.get_string('gumnotallowed_title', 'tinymce_recordrtc'),
103
                                    message: M.util.get_string('gumnotallowed', 'tinymce_recordrtc')
104
                                });
105
                            });
106
107
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
108
                            break;
109
                        case 'NotFoundError':
110
                            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...
111
                                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
112
                                    title: M.util.get_string('gumnotfound_title', 'tinymce_recordrtc'),
113
                                    message: M.util.get_string('gumnotfound', 'tinymce_recordrtc')
114
                                });
115
                            });
116
117
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
118
                            break;
119
                        case 'NotReadableError':
120
                            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...
121
                                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
122
                                    title: M.util.get_string('gumnotreadable_title', 'tinymce_recordrtc'),
123
                                    message: M.util.get_string('gumnotreadable', 'tinymce_recordrtc')
124
                                });
125
                            });
126
127
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
128
                            break;
129
                        case 'OverConstrainedError':
130
                            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...
131
                                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
132
                                    title: M.util.get_string('gumoverconstrained_title', 'tinymce_recordrtc'),
133
                                    message: M.util.get_string('gumoverconstrained', 'tinymce_recordrtc')
134
                                });
135
                            });
136
137
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
138
                            break;
139
                        case 'SecurityError':
140
                            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...
141
                                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
142
                                    title: M.util.get_string('gumsecurity_title', 'tinymce_recordrtc'),
143
                                    message: M.util.get_string('gumsecurity', 'tinymce_recordrtc')
144
                                });
145
                            });
146
147
                            cm.editorScope.closeDialogue(cm.editorScope);
0 ignored issues
show
Bug introduced by
The variable cm seems to be never declared. If this is a global, consider adding a /** global: cm */ 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...
148
                            break;
149
                        case 'TypeError':
150
                            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...
151
                                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
152
                                    title: M.util.get_string('gumtype_title', 'tinymce_recordrtc'),
153
                                    message: M.util.get_string('gumtype', 'tinymce_recordrtc')
154
                                });
155
                            });
156
157
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
158
                    }
159
160
                    // Proceed to treat as a stopped recording.
161
                    commonConfig.onMediaStopped(btnLabel);
162
                }
163
            };
164
165
            // Show video tag without controls to view webcam stream.
166
            player.parentElement.parentElement.classList.remove('hide');
167
            player.controls = false;
168
169
            // Capture audio+video stream from webcam/microphone.
170
            M.tinymce_recordrtc.capture_audio_video(commonConfig);
171
172
            // When audio+video stream is successfully captured, start recording.
173
            btn.mediaCapturedCallback = function() {
174
                M.tinymce_recordrtc.start_recording(recType, btn.stream);
175
            };
176
        } else { // If button is displaying "Stop Recording".
177
            // First of all clears the countdownTicker.
178
            clearInterval(countdownTicker);
179
180
            // Disable "Record Again" button for 1s to allow background processing (closing streams).
181
            setTimeout(function() {
182
                btn.disabled = false;
183
            }, 1000);
184
185
            // Stop recording.
186
            M.tinymce_recordrtc.stop_recording_video(btn.stream);
187
188
            // Change button to offer to record again.
189
            btn.textContent = M.util.get_string('recordagain', 'tinymce_recordrtc');
190
            if (!recordrtc.oldermoodle) {
191
                startStopBtn.classList.remove('btn-danger');
192
                startStopBtn.classList.add('btn-outline-danger');
193
            }
194
        }
195
    };
196
};
197
198
// Setup to get audio+video stream from microphone/webcam.
199
M.tinymce_recordrtc.capture_audio_video = function(config) {
200
    M.tinymce_recordrtc.capture_user_media(
201
        // Media constraints.
202
        {
203
            audio: true,
204
            video: {
205
                width: {ideal: 640},
206
                height: {ideal: 480}
207
            }
208
        },
209
210
        // Success callback.
211
        function(audioVideoStream) {
212
            // Set video player source to microphone+webcam stream, and play it back as it's recording.
213
            player.srcObject = audioVideoStream;
214
            player.play();
215
216
            config.onMediaCaptured(audioVideoStream);
217
        },
218
219
        // Error callback.
220
        function(error) {
221
            config.onMediaCapturingFailed(error);
222
        }
223
    );
224
};
225
226
M.tinymce_recordrtc.stop_recording_video = function(stream) {
227
    // Stop recording microphone stream.
228
    mediaRecorder.stop();
229
230
    // Stop each individual MediaTrack.
231
    stream.getTracks().forEach(function(track) {
232
        track.stop();
233
    });
234
235
    // Set source of video player.
236
    var blob = new Blob(chunks, {type: mediaRecorder.mimeType});
237
    player.src = URL.createObjectURL(blob);
238
239
    // Enable controls for video player, and unmute.
240
    player.muted = false;
241
    player.controls = true;
242
243
    // Show upload button.
244
    uploadBtn.parentElement.parentElement.classList.remove('hide');
245
    uploadBtn.textContent = M.util.get_string('attachrecording', 'tinymce_recordrtc');
246
    uploadBtn.disabled = false;
247
248
    // Handle when upload button is clicked.
249
    uploadBtn.onclick = function() {
250
        // Trigger error if no recording has been made.
251
        if (!player.src || chunks === []) {
252
            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...
253
                new M.core.alert({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new M.core.alert({Identi...(tinymce_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...
254
                    title: M.util.get_string('norecordingfound_title', 'tinymce_recordrtc'),
255
                    message: M.util.get_string('norecordingfound', 'tinymce_recordrtc')
256
                });
257
            });
258
        } else {
259
            var btn = uploadBtn;
260
            btn.disabled = true;
261
262
            // Upload recording to server.
263
            M.tinymce_recordrtc.upload_to_server(recType, function(progress, fileURLOrError) {
264
                if (progress === 'ended') { // Insert annotation in text.
265
                    btn.disabled = false;
266
                    M.tinymce_recordrtc.insert_annotation(recType, fileURLOrError);
267
                } else if (progress === 'upload-failed') { // Show error message in upload button.
268
                    btn.disabled = false;
269
                    btn.textContent = M.util.get_string('uploadfailed', 'tinymce_recordrtc') + ' ' + fileURLOrError;
270
                } else if (progress === 'upload-failed-404') { // 404 error = File too large in Moodle.
271
                    btn.disabled = false;
272
                    btn.textContent = M.util.get_string('uploadfailed404', 'tinymce_recordrtc');
273
                } else if (progress === 'upload-aborted') {
274
                    btn.disabled = false;
275
                    btn.textContent = M.util.get_string('uploadaborted', 'tinymce_recordrtc') + ' ' + fileURLOrError;
276
                } else {
277
                    btn.textContent = progress;
278
                }
279
            });
280
        }
281
    };
282
};
283