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

tinymce/js/audiomodule.js   B

Complexity

Total Complexity 41
Complexity/F 1.86

Size

Lines of Code 248
Function Count 22

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 41
dl 0
loc 248
rs 8.2769
c 4
b 1
f 0
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  
A M.tinymce_recordrtc.capture_audio 0 21 1
A M.tinymce_recordrtc.stop_recording_audio 0 58 1

How to fix   Complexity   

Complexity

Complex classes like tinymce/js/audiomodule.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('audio#player');
33
    startStopBtn = document.querySelector('button#start-stop');
34
    uploadBtn = document.querySelector('button#upload');
35
    recType = 'audio';
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 audio player and upload button are not shown.
54
            player.parentElement.parentElement.classList.add('hide');
55
            uploadBtn.parentElement.parentElement.classList.add('hide');
56
57
            // Change look of recording button.
58
            if (!recordrtc.oldermoodle) {
59
                startStopBtn.classList.remove('btn-outline-danger');
60
                startStopBtn.classList.add('btn-danger');
61
            }
62
63
            // Empty the array containing the previously recorded chunks.
64
            chunks = [];
65
            blobSize = 0;
66
67
            // Initialize common configurations.
68
            var commonConfig = {
69
                // When the stream is captured from the microphone/webcam.
70
                onMediaCaptured: function(stream) {
71
                    // Make audio stream available at a higher level by making it a property of btn.
72
                    btn.stream = stream;
73
74
                    if (btn.mediaCapturedCallback) {
75
                        btn.mediaCapturedCallback();
76
                    }
77
                },
78
79
                // Revert button to "Record Again" when recording is stopped.
80
                onMediaStopped: function(btnLabel) {
81
                    btn.textContent = btnLabel;
82
                },
83
84
                // Handle recording errors.
85
                onMediaCapturingFailed: function(error) {
86
                    var btnLabel = null;
87
88
                    // Handle getUserMedia-thrown errors.
89
                    switch (error.name) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
90
                        case 'AbortError':
91
                            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...
92
                                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...
93
                                    title: M.util.get_string('gumabort_title', 'tinymce_recordrtc'),
94
                                    message: M.util.get_string('gumabort', 'tinymce_recordrtc')
95
                                });
96
                            });
97
98
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
99
                            break;
100
                        case 'NotAllowedError':
101
                            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...
102
                                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...
103
                                    title: M.util.get_string('gumnotallowed_title', 'tinymce_recordrtc'),
104
                                    message: M.util.get_string('gumnotallowed', 'tinymce_recordrtc')
105
                                });
106
                            });
107
108
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
109
                            break;
110
                        case 'NotFoundError':
111
                            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...
112
                                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...
113
                                    title: M.util.get_string('gumnotfound_title', 'tinymce_recordrtc'),
114
                                    message: M.util.get_string('gumnotfound', 'tinymce_recordrtc')
115
                                });
116
                            });
117
118
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
119
                            break;
120
                        case 'NotReadableError':
121
                            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...
122
                                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...
123
                                    title: M.util.get_string('gumnotreadable_title', 'tinymce_recordrtc'),
124
                                    message: M.util.get_string('gumnotreadable', 'tinymce_recordrtc')
125
                                });
126
                            });
127
128
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
129
                            break;
130
                        case 'OverConstrainedError':
131
                            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...
132
                                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...
133
                                    title: M.util.get_string('gumoverconstrained_title', 'tinymce_recordrtc'),
134
                                    message: M.util.get_string('gumoverconstrained', 'tinymce_recordrtc')
135
                                });
136
                            });
137
138
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
139
                            break;
140
                        case 'SecurityError':
141
                            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...
142
                                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...
143
                                    title: M.util.get_string('gumsecurity_title', 'tinymce_recordrtc'),
144
                                    message: M.util.get_string('gumsecurity', 'tinymce_recordrtc')
145
                                });
146
                            });
147
148
                            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...
149
                            break;
150
                        case 'TypeError':
151
                            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...
152
                                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...
153
                                    title: M.util.get_string('gumtype_title', 'tinymce_recordrtc'),
154
                                    message: M.util.get_string('gumtype', 'tinymce_recordrtc')
155
                                });
156
                            });
157
158
                            btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
159
                    }
160
161
                    // Proceed to treat as a stopped recording.
162
                    commonConfig.onMediaStopped(btnLabel);
163
                }
164
            };
165
166
            // Capture audio stream from microphone.
167
            M.tinymce_recordrtc.capture_audio(commonConfig);
168
169
            // When audio stream is successfully captured, start recording.
170
            btn.mediaCapturedCallback = function() {
171
                M.tinymce_recordrtc.start_recording(recType, btn.stream);
172
            };
173
        } else { // If button is displaying "Stop Recording".
174
            // First of all clears the countdownTicker.
175
            clearInterval(countdownTicker);
176
177
            // Disable "Record Again" button for 1s to allow background processing (closing streams).
178
            setTimeout(function() {
179
                btn.disabled = false;
180
            }, 1000);
181
182
            // Stop recording.
183
            M.tinymce_recordrtc.stop_recording_audio(btn.stream);
184
185
            // Change button to offer to record again.
186
            btn.textContent = M.util.get_string('recordagain', 'tinymce_recordrtc');
187
            if (!recordrtc.oldermoodle) {
188
                startStopBtn.classList.remove('btn-danger');
189
                startStopBtn.classList.add('btn-outline-danger');
190
            }
191
        }
192
    };
193
};
194
195
// Setup to get audio stream from microphone.
196
M.tinymce_recordrtc.capture_audio = function(config) {
197
    M.tinymce_recordrtc.capture_user_media(
198
        // Media constraints.
199
        {
200
            audio: true
201
        },
202
203
        // Success callback.
204
        function(audioStream) {
205
            // Set audio player source to microphone stream.
206
            player.srcObject = audioStream;
207
208
            config.onMediaCaptured(audioStream);
209
        },
210
211
        // Error callback.
212
        function(error) {
213
            config.onMediaCapturingFailed(error);
214
        }
215
    );
216
};
217
218
M.tinymce_recordrtc.stop_recording_audio = function(stream) {
219
    // Stop recording microphone stream.
220
    mediaRecorder.stop();
221
222
    // Stop each individual MediaTrack.
223
    stream.getTracks().forEach(function(track) {
224
        track.stop();
225
    });
226
227
    // Set source of audio player.
228
    var blob = new Blob(chunks, {type: mediaRecorder.mimeType});
229
    player.src = URL.createObjectURL(blob);
230
231
    // Show audio player with controls enabled, and unmute.
232
    player.muted = false;
233
    player.controls = true;
234
    player.parentElement.parentElement.classList.remove('hide');
235
236
    // Show upload button.
237
    uploadBtn.parentElement.parentElement.classList.remove('hide');
238
    uploadBtn.textContent = M.util.get_string('attachrecording', 'tinymce_recordrtc');
239
    uploadBtn.disabled = false;
240
241
    // Handle when upload button is clicked.
242
    uploadBtn.onclick = function() {
243
        // Trigger error if no recording has been made.
244
        if (!player.src || 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...(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...
247
                    title: M.util.get_string('norecordingfound_title', 'tinymce_recordrtc'),
248
                    message: M.util.get_string('norecordingfound', 'tinymce_recordrtc')
249
                });
250
            });
251
        } else {
252
            var btn = uploadBtn;
253
            btn.disabled = true;
254
255
            // Upload recording to server.
256
            M.tinymce_recordrtc.upload_to_server(recType, function(progress, fileURLOrError) {
257
                if (progress === 'ended') { // Insert annotation in text.
258
                    btn.disabled = false;
259
                    M.tinymce_recordrtc.insert_annotation(recType, fileURLOrError);
260
                } else if (progress === 'upload-failed') { // Show error message in upload button.
261
                    btn.disabled = false;
262
                    btn.textContent = M.util.get_string('uploadfailed', 'tinymce_recordrtc') + ' ' + fileURLOrError;
263
                } else if (progress === 'upload-failed-404') { // 404 error = File too large in Moodle.
264
                    btn.disabled = false;
265
                    btn.textContent = M.util.get_string('uploadfailed404', 'tinymce_recordrtc');
266
                } else if (progress === 'upload-aborted') {
267
                    btn.disabled = false;
268
                    btn.textContent = M.util.get_string('uploadaborted', 'tinymce_recordrtc') + ' ' + fileURLOrError;
269
                } else {
270
                    btn.textContent = progress;
271
                }
272
            });
273
        }
274
    };
275
};
276