GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#11)
by
unknown
01:40
created

tinymce/js/audiomodule.js   C

Complexity

Total Complexity 58
Complexity/F 1.71

Size

Lines of Code 413
Function Count 34

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 0
nc 4224
dl 0
loc 413
rs 5.4871
c 2
b 0
f 0
wmc 58
mnd 3
bc 54
fnc 34
bpm 1.5882
cpm 1.7058
noi 27

15 Functions

Rating   Name   Duplication   Size   Complexity  
B 0 21 5
A audiomodule.js ➔ d 0 3 1
A M.tinymce_recordrtc.startRecording 0 19 1
A M.tinymce_recordrtc.stopRecording 0 63 1
B M.tinymce_recordrtc.uploadToServer 0 42 1
A M.tinymce_recordrtc.pad 0 9 2
A M.tinymce_recordrtc.create_annotation 0 12 1
B M.tinymce_recordrtc.captureAudio 0 25 1
A M.tinymce_recordrtc.handleStop 0 3 1
A M.tinymce_recordrtc.setTime 0 10 2
B M.tinymce_recordrtc.makeXMLHttpRequest 0 31 1
B M.tinymce_recordrtc.view_init 0 120 2
A M.tinymce_recordrtc.handleDataAvailable 0 3 1
A M.tinymce_recordrtc.captureUserMedia 0 3 1
A M.tinymce_recordrtc.insert_annotation 0 6 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: URL */
9
/** global: params */
10
/** global: initialized variables */
11
12
M.tinymce_recordrtc = M.tinymce_recordrtc || {};
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
13
14
// Extract plugin settings to params hash.
15
(function() {
16
    var params = {};
17
    var r = /([^&=]+)=?([^&]*)/g;
18
19
    var d = function(s) {
20
        return decodeURIComponent(s.replace(/\+/g, ' '));
21
    };
22
23
    var search = window.location.search;
24
    var match = r.exec(search.substring(1));
25
    while (match) {
26
        params[d(match[1])] = d(match[2]);
27
28
        if (d(match[2]) === 'true' || d(match[2]) === 'false') {
29
            params[d(match[1])] = d(match[2]) === 'true' ? true : false;
30
        }
31
        match = r.exec(search.substring(1));
32
    }
33
34
    window.params = params;
35
})();
36
37
// Initialize some variables.
38
var player = null;
39
var startStopBtn = null;
40
var uploadBtn = null;
41
var countdownSeconds = null;
42
var countdownTicker = null;
43
var mediaRecorder = null;
44
var chunks = null;
45
46
/**
47
 * This function is initialized from PHP
48
 *
49
 * @param {Object}
0 ignored issues
show
Documentation introduced by
The parameter * does not exist. Did you maybe forget to remove this comment?
Loading history...
50
 *            Y YUI instance
51
 */
52
M.tinymce_recordrtc.view_init = function() {
53
    // Assignment of global variables.
54
    player = document.querySelector('audio#player');
55
    startStopBtn = document.querySelector('button#start-stop');
56
    uploadBtn = document.querySelector('button#upload');
57
58
    // Display "consider switching browsers" message if not using:
59
    // - Firefox 29+;
60
    // - Chrome 49+;
61
    // - Opera 36+.
62
    if (!((bowser.firefox && bowser.version >= 29) ||
0 ignored issues
show
Bug introduced by
The variable bowser seems to be never declared. If this is a global, consider adding a /** global: bowser */ 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...
63
          (bowser.chrome && bowser.version >= 49) ||
64
          (bowser.opera && bowser.version >= 36))) {
65
        var alert = document.querySelector('div[id=alert-warning]');
66
        alert.parentElement.parentElement.classList.remove('hide');
67
    }
68
69
    // Run when user clicks on "record" button.
70
    startStopBtn.onclick = function() {
71
        var btn = this;
72
        btn.disabled = true;
73
74
        // If button is displaying "Start Recording" or "Record Again".
75
        if ((btn.textContent === M.util.get_string('startrecording', 'tinymce_recordrtc')) ||
76
            (btn.textContent === M.util.get_string('recordagain', 'tinymce_recordrtc')) ||
77
            (btn.textContent === M.util.get_string('recordingfailed', 'tinymce_recordrtc'))) {
78
            // Hide alert-danger if it is shown.
79
            var alert = document.querySelector('div[id=alert-danger]');
80
            alert.parentElement.parentElement.classList.add('hide');
81
82
            // Make sure the audio player and upload button is not shown.
83
            player.parentElement.parentElement.classList.add('hide');
84
            uploadBtn.parentElement.parentElement.classList.add('hide');
85
86
            // Change look of recording button.
87
            startStopBtn.classList.remove('btn-outline-danger');
88
            startStopBtn.classList.add('btn-danger');
89
90
            // Empty the array containing the previously recorded chunks.
91
            chunks = [];
92
93
            // Initialize common configurations.
94
            var commonConfig = {
95
                // When the stream is captured from the microphone/webcam.
96
                onMediaCaptured: function(stream) {
97
                    // Make audio stream available at a higher level by making it a property of btn.
98
                    btn.stream = stream;
99
100
                    if (btn.mediaCapturedCallback) {
101
                        btn.mediaCapturedCallback();
102
                    }
103
                },
104
105
                // Revert button to "Record Again" when recording is stopped.
106
                onMediaStopped: function(btnLabel) {
107
                    btn.textContent = btnLabel;
108
                },
109
110
                // Handle recording errors.
111
                onMediaCapturingFailed: function(error) {
112
                    var btnLabel = null;
113
114
                    // If Firefox and Permission Denied error.
115
                    if ((error.name === 'PermissionDeniedError') && bowser.firefox) {
0 ignored issues
show
Bug introduced by
The variable bowser seems to be never declared. If this is a global, consider adding a /** global: bowser */ 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...
116
                        InstallTrigger.install({
0 ignored issues
show
Bug introduced by
The variable InstallTrigger seems to be never declared. If this is a global, consider adding a /** global: InstallTrigger */ 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...
117
                            'Foo': {
118
                                // Link: https://addons.mozilla.org/firefox/downloads/latest/655146/addon-655146...
119
                                // ...-latest.xpi?src=dp-btn-primary.
120
                                URL: 'https://addons.mozilla.org/en-US/firefox/addon/enable-screen-capturing/',
121
                                toString: function() {
122
                                    return this.URL;
123
                                }
124
                            }
125
                        });
126
127
                        btnLabel = M.util.get_string('startrecording', 'tinymce_recordrtc');
128
                    } else if ((error.name === 'DevicesNotFoundError') ||
129
                               (error.name === 'NotFoundError')) { // If Device Not Found error.
130
                        var alert = document.querySelector('div[id=alert-danger]');
131
                        alert.parentElement.parentElement.classList.remove('hide');
132
                        alert.textContent = M.util.get_string('inputdevicealert_title', 'tinymce_recordrtc') + ' ' + M.util.get_string('inputdevicealert', 'tinymce_recordrtc');
133
134
                        btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
135
                    }
136
137
                    // Proceed to treat as a stopped recording.
138
                    commonConfig.onMediaStopped(btnLabel);
139
                }
140
            };
141
142
            // Capture audio stream from microphone.
143
            M.tinymce_recordrtc.captureAudio(commonConfig);
144
145
            // When audio stream is successfully captured, start recording.
146
            btn.mediaCapturedCallback = function() {
147
                M.tinymce_recordrtc.startRecording(btn.stream);
148
            };
149
150
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
151
        } else { // If button is displaying "Stop Recording".
152
            // First of all clears the countdownTicker.
153
            clearInterval(countdownTicker);
154
155
            // Disable "Record Again" button for 1s to allow background processing (closing streams).
156
            setTimeout(function() {
157
                btn.disabled = false;
158
            }, 1000);
159
160
            // Stop recording.
161
            M.tinymce_recordrtc.stopRecording(btn.stream);
162
163
            // Change button to offer to record again.
164
            btn.textContent = M.util.get_string('recordagain', 'tinymce_recordrtc');
165
            startStopBtn.classList.remove('btn-danger');
166
            startStopBtn.classList.add('btn-outline-danger');
167
168
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
169
        }
170
    };
171
};
172
173
/////////////////////////
174
// Functions for capturing, recording, and uploading stream.
175
/////////////////////////
176
177
// Capture webcam/microphone stream.
178
M.tinymce_recordrtc.captureUserMedia = function(mediaConstraints, successCallback, errorCallback) {
179
    navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ 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...
180
};
181
182
// Setup to get audio stream from microphone.
183
M.tinymce_recordrtc.captureAudio = function(config) {
184
    M.tinymce_recordrtc.captureUserMedia(
185
        // Media constraints.
186
        {
187
            audio: true
188
        },
189
190
        // Success callback.
191
        function(audioStream) {
192
            console.log('getUserMedia() got stream:', audioStream);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
193
194
            // Set audio player to play microphone stream.
195
            player.srcObject = audioStream;
196
            player.play();
197
198
            config.onMediaCaptured(audioStream);
199
        },
200
201
        // Error callback.
202
        function(error) {
203
            console.log('getUserMedia() error:', error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
204
            config.onMediaCapturingFailed(error);
205
        }
206
    );
207
};
208
209
// Add chunks of audio to array when made available.
210
M.tinymce_recordrtc.handleDataAvailable = function(event) {
211
    chunks.push(event.data);
212
};
213
214
// Output information to console when recording stopped.
215
M.tinymce_recordrtc.handleStop = function(event) {
216
    console.log('MediaRecorder stopped:', event);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
217
};
218
219
M.tinymce_recordrtc.startRecording = function(stream) {
220
    // Initialize recording of stream.
221
    mediaRecorder = new MediaRecorder(stream);
0 ignored issues
show
Bug introduced by
The variable MediaRecorder seems to be never declared. If this is a global, consider adding a /** global: MediaRecorder */ 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...
222
    console.log('Created MediaRecorder:', mediaRecorder);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
223
224
    mediaRecorder.ondataavailable = M.tinymce_recordrtc.handleDataAvailable;
225
    mediaRecorder.onstop = M.tinymce_recordrtc.handleStop;
226
    mediaRecorder.start(10); // Capture in 10ms chunks. Must be set to work with Firefox.
227
    console.log('MediaRecorder started:', mediaRecorder);
228
229
    // Set recording timer to the time specified in the settings.
230
    countdownSeconds = params['timelimit'];
231
    countdownSeconds++;
232
    startStopBtn.innerHTML = M.util.get_string('stoprecording', 'tinymce_recordrtc') +
233
                             ' (<span id="minutes"></span>:<span id="seconds"></span>)';
234
    M.tinymce_recordrtc.setTime();
235
    startStopBtn.disabled = false;
236
    countdownTicker = setInterval(M.tinymce_recordrtc.setTime, 1000);
237
};
238
239
M.tinymce_recordrtc.stopRecording = function(stream) {
240
    mediaRecorder.stop();
241
242
    stream.getTracks().forEach(function(track) {
243
        track.stop();
244
        console.log('MediaTrack stopped:', track);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
245
    });
246
247
    // Set source of audio player, then show it with controls enabled.
248
    // Not sure if necessary, need to figure out what formats the different browsers record in.
249
    /*
250
    if (webrtcDetectedBrowser === 'firefox') {
251
      var blob = new Blob(chunks, {type: 'audio/ogg'});
252
    } else {
253
      var blob = new Blob(chunks, {type: 'audio/wav'});
254
    }
255
    */
256
    var blob = new Blob(chunks, {
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...
257
        type: 'audio/ogg;codecs=opus'
258
    });
259
    player.src = URL.createObjectURL(blob);
260
261
    player.muted = false;
262
    player.controls = true;
263
    player.parentElement.parentElement.classList.remove('hide');
264
    player.play();
265
266
    player.onended = function() {
267
        player.pause();
268
    };
269
270
    // Show upload button.
271
    uploadBtn.parentElement.parentElement.classList.remove('hide');
272
    uploadBtn.textContent = M.util.get_string('attachrecording', 'tinymce_recordrtc');
273
    uploadBtn.disabled = false;
274
275
    // Handle when upload button is clicked.
276
    uploadBtn.onclick = function() {
277
        // Trigger error if no recording has been made.
278
        if (!player.src || chunks === []) {
279
            return alert(M.util.get_string('norecordingfound', 'tinymce_recordrtc'));
280
        }
281
282
        var btn = uploadBtn;
283
        btn.disabled = true;
284
285
        // Upload recording to server.
286
        M.tinymce_recordrtc.uploadToServer(function(progress, fileURL) {
287
            if (progress === 'ended') {
288
                btn.disabled = false;
289
                M.tinymce_recordrtc.insert_annotation(fileURL);
290
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
291
            } else if (progress === 'upload-failed') {
292
                btn.disabled = false;
293
                btn.textContent = M.util.get_string('uploadfailed', 'tinymce_recordrtc');
294
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
295
            } else {
296
                btn.textContent = progress;
297
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
298
            }
299
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
300
    };
301
};
302
303
// Upload recorded audio to server.
304
M.tinymce_recordrtc.uploadToServer = function(callback) {
305
    var xhr = new XMLHttpRequest();
0 ignored issues
show
Bug introduced by
The variable XMLHttpRequest seems to be never declared. If this is a global, consider adding a /** global: XMLHttpRequest */ 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...
306
307
    // Get src URL of audio tag.
308
    xhr.open('GET', player.src, true);
309
    xhr.responseType = 'blob';
310
311
    xhr.onload = function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
312
        if (xhr.status === 200) {
313
            var date = new Date();
0 ignored issues
show
Unused Code introduced by
The variable date seems to be never used. Consider removing it.
Loading history...
314
315
            // Variable blob is now the blob that the audio tag's src pointed to.
316
            var blob = this.response;
317
            // Generate filename with random ID and file extension.
318
            var fileName = (Math.random() * 1000).toString().replace('.', '');
319
            // Not sure if necessary, need to figure out what formats the different browsers record in:
320
            // fileName += '.' + (webrtcDetectedBrowser === 'firefox' ? '-audio.ogg' : '-audio.wav');.
321
            fileName += '-audio.ogg';
322
323
            // Create FormData to send to PHP upload/save script.
324
            var formData = new FormData();
325
            formData.append('contextid', recordrtc.contextid);
0 ignored issues
show
Bug introduced by
The variable recordrtc seems to be never declared. If this is a global, consider adding a /** global: recordrtc */ 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...
326
            formData.append('sesskey', parent.M.cfg.sesskey);
0 ignored issues
show
Bug introduced by
The variable parent seems to be never declared. If this is a global, consider adding a /** global: parent */ 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...
327
            formData.append('audio-filename', fileName);
328
            formData.append('audio-blob', blob);
329
330
            // Pass FormData to PHP script using XHR.
331
            M.tinymce_recordrtc.makeXMLHttpRequest('save.php', formData, function(progress, responseText) {
332
                if (progress === 'upload-ended') {
333
                    var initialURL = location.href.replace(location.href.split('/').pop(), '') + 'uploads.php/';
334
                    callback('ended', initialURL + responseText);
335
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
336
                } else {
337
                    callback(progress);
338
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
339
                }
340
            });
341
        }
342
    };
343
344
    xhr.send();
345
}
346
347
// Handle XHR sending/receiving/status.
348
M.tinymce_recordrtc.makeXMLHttpRequest = function(url, data, callback) {
349
    var xhr = new XMLHttpRequest();
0 ignored issues
show
Bug introduced by
The variable XMLHttpRequest seems to be never declared. If this is a global, consider adding a /** global: XMLHttpRequest */ 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...
350
351
    xhr.onreadystatechange = function() {
352
        // When request is finished and successful.
353
        if ((xhr.readyState === 4) && (xhr.status === 200)) {
354
            callback('upload-ended', xhr.responseText);
355
        } else if (xhr.status === 404) { // When request returns 404 Not Found.
356
            callback('upload-failed');
357
        }
358
    };
359
360
    xhr.upload.onprogress = function(event) {
361
        callback(Math.round(event.loaded / event.total * 100) + "% " +
362
                 M.util.get_string('uploadprogress', 'tinymce_recordrtc'));
363
    };
364
365
    xhr.upload.onerror = function(error) {
366
        callback('upload-failed');
367
        console.error('XMLHttpRequest failed: ', error);
368
    };
369
370
    xhr.upload.onabort = function(error) {
371
        callback(M.util.get_string('uploadaborted', 'tinymce_recordrtc'));
372
        console.error('XMLHttpRequest aborted: ', error);
373
    };
374
375
    // POST FormData to PHP script that handles uploading/saving.
376
    xhr.open('POST', url);
377
    xhr.send(data);
378
}
379
380
// Makes 1min and 2s display as 1:02 on timer instead of 1:2, for example.
381
M.tinymce_recordrtc.pad = function(val) {
382
    var valString = val + "";
383
384
    if (valString.length < 2) {
385
        return "0" + valString;
386
    } else {
387
        return valString;
388
    }
389
};
390
391
// Functionality to make recording timer count down.
392
// Also makes recording stop when time limit is hit.
393
M.tinymce_recordrtc.setTime = function() {
394
    countdownSeconds--;
395
396
    startStopBtn.querySelector('span#seconds').textContent = M.tinymce_recordrtc.pad(countdownSeconds % 60);
397
    startStopBtn.querySelector('span#minutes').textContent = M.tinymce_recordrtc.pad(parseInt(countdownSeconds / 60));
398
399
    if (countdownSeconds === 0) {
400
        startStopBtn.click();
401
    }
402
};
403
404
// Generates link to recorded annotation.
405
M.tinymce_recordrtc.create_annotation = function(recording_url) {
406
    // Create an icon linked to file in both editor text area and submission page:
407
    // var annotation = '<div id="recordrtc_annotation" class="text-center">...
408
    // ...<a target="_blank" href="' + recording_url + '"><img alt="RecordRTC Annotation"...
409
    // ...title="RecordRTC Annotation" src="' + recordrtc.icon32 + '" /></a></div>';.
410
411
    // Create link to file in editor text area and audio player in submission page.
412
    var annotation = '<div id="recordrtc_annotation" class="text-center"><a target="_blank" href="' + recording_url + '">' +
413
                     M.util.get_string('annotation', 'tinymce_recordrtc') + '</a></div>';
414
415
    return annotation;
416
};
417
418
// Inserts link to annotation in editor text area.
419
M.tinymce_recordrtc.insert_annotation = function(recording_url) {
420
    var annotation = M.tinymce_recordrtc.create_annotation(recording_url);
421
422
    tinyMCEPopup.editor.execCommand('mceInsertContent', false, annotation);
0 ignored issues
show
Bug introduced by
The variable tinyMCEPopup seems to be never declared. If this is a global, consider adding a /** global: tinyMCEPopup */ 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...
423
    tinyMCEPopup.close();
424
};
425