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:47
created

tinymce/js/audiomodule.js   C

Complexity

Total Complexity 58
Complexity/F 1.71

Size

Lines of Code 414
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 414
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  
A M.tinymce_recordrtc.startRecording 0 19 1
A M.tinymce_recordrtc.stopRecording 0 63 1
B M.tinymce_recordrtc.uploadToServer 0 42 1
B 0 21 5
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 audiomodule.js ➔ d 0 3 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 121 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-info]');
66
        alert.textContent = M.util.get_string('browseralert', 'tinymce_recordrtc');
67
        alert.classList.remove('hide');
68
    }
69
70
    // Run when user clicks on "record" button.
71
    startStopBtn.onclick = function() {
72
        var btn = this;
73
        btn.disabled = true;
74
75
        // If button is displaying "Start Recording" or "Record Again".
76
        if ((btn.textContent === M.util.get_string('startrecording', 'tinymce_recordrtc')) ||
77
            (btn.textContent === M.util.get_string('recordagain', 'tinymce_recordrtc')) ||
78
            (btn.textContent === M.util.get_string('recordingfailed', 'tinymce_recordrtc'))) {
79
            // Hide alert-danger if it is shown.
80
            var alert = document.querySelector('div[id=alert-danger]');
81
            alert.classList.add('hide');
82
83
            // Make sure the audio player and upload button is not shown.
84
            player.classList.add('hide');
85
            uploadBtn.parentElement.classList.add('hide');
86
87
            // Change look of recording button.
88
            startStopBtn.classList.remove('btn-outline-danger');
89
            startStopBtn.classList.add('btn-danger');
90
91
            // Empty the array containing the previously recorded chunks.
92
            chunks = [];
93
94
            // Initialize common configurations.
95
            var commonConfig = {
96
                // When the stream is captured from the microphone/webcam.
97
                onMediaCaptured: function(stream) {
98
                    // Make audio stream available at a higher level by making it a property of btn.
99
                    btn.stream = stream;
100
101
                    if (btn.mediaCapturedCallback) {
102
                        btn.mediaCapturedCallback();
103
                    }
104
                },
105
106
                // Revert button to "Record Again" when recording is stopped.
107
                onMediaStopped: function(btnLabel) {
108
                    btn.textContent = btnLabel;
109
                },
110
111
                // Handle recording errors.
112
                onMediaCapturingFailed: function(error) {
113
                    var btnLabel = null;
114
115
                    // If Firefox and Permission Denied error.
116
                    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...
117
                        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...
118
                            'Foo': {
119
                                // Link: https://addons.mozilla.org/firefox/downloads/latest/655146/addon-655146...
120
                                // ...-latest.xpi?src=dp-btn-primary.
121
                                URL: 'https://addons.mozilla.org/en-US/firefox/addon/enable-screen-capturing/',
122
                                toString: function() {
123
                                    return this.URL;
124
                                }
125
                            }
126
                        });
127
128
                        btnLabel = M.util.get_string('startrecording', 'tinymce_recordrtc');
129
                    } else if ((error.name === 'DevicesNotFoundError') ||
130
                               (error.name === 'NotFoundError')) { // If Device Not Found error.
131
                        var alert = document.querySelector('div[id=alert-danger]');
132
                        alert.classList.remove('hide');
133
                        alert.textContent = M.util.get_string('inputdevicealert', 'tinymce_recordrtc');
134
135
                        btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
136
                    }
137
138
                    // Proceed to treat as a stopped recording.
139
                    commonConfig.onMediaStopped(btnLabel);
140
                }
141
            };
142
143
            // Capture audio stream from microphone.
144
            M.tinymce_recordrtc.captureAudio(commonConfig);
145
146
            // When audio stream is successfully captured, start recording.
147
            btn.mediaCapturedCallback = function() {
148
                M.tinymce_recordrtc.startRecording(btn.stream);
149
            };
150
151
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
152
        } else { // If button is displaying "Stop Recording".
153
            // First of all clears the countdownTicker.
154
            clearInterval(countdownTicker);
155
156
            // Disable "Record Again" button for 1s to allow background processing (closing streams).
157
            setTimeout(function() {
158
                btn.disabled = false;
159
            }, 1000);
160
161
            // Stop recording.
162
            M.tinymce_recordrtc.stopRecording(btn.stream);
163
164
            // Change button to offer to record again.
165
            btn.textContent = M.util.get_string('recordagain', 'tinymce_recordrtc');
166
            startStopBtn.classList.remove('btn-danger');
167
            startStopBtn.classList.add('btn-outline-danger');
168
169
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
170
        }
171
    };
172
};
173
174
/////////////////////////
175
// Functions for capturing, recording, and uploading stream.
176
/////////////////////////
177
178
// Capture webcam/microphone stream.
179
M.tinymce_recordrtc.captureUserMedia = function(mediaConstraints, successCallback, errorCallback) {
180
    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...
181
};
182
183
// Setup to get audio stream from microphone.
184
M.tinymce_recordrtc.captureAudio = function(config) {
185
    M.tinymce_recordrtc.captureUserMedia(
186
        // Media constraints.
187
        {
188
            audio: true
189
        },
190
191
        // Success callback.
192
        function(audioStream) {
193
            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...
194
195
            // Set audio player to play microphone stream.
196
            player.srcObject = audioStream;
197
            player.play();
198
199
            config.onMediaCaptured(audioStream);
200
        },
201
202
        // Error callback.
203
        function(error) {
204
            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...
205
            config.onMediaCapturingFailed(error);
206
        }
207
    );
208
};
209
210
// Add chunks of audio to array when made available.
211
M.tinymce_recordrtc.handleDataAvailable = function(event) {
212
    chunks.push(event.data);
213
};
214
215
// Output information to console when recording stopped.
216
M.tinymce_recordrtc.handleStop = function(event) {
217
    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...
218
};
219
220
M.tinymce_recordrtc.startRecording = function(stream) {
221
    // Initialize recording of stream.
222
    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...
223
    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...
224
225
    mediaRecorder.ondataavailable = M.tinymce_recordrtc.handleDataAvailable;
226
    mediaRecorder.onstop = M.tinymce_recordrtc.handleStop;
227
    mediaRecorder.start(10); // Capture in 10ms chunks. Must be set to work with Firefox.
228
    console.log('MediaRecorder started:', mediaRecorder);
229
230
    // Set recording timer to the time specified in the settings.
231
    countdownSeconds = params['timelimit'];
232
    countdownSeconds++;
233
    startStopBtn.innerHTML = M.util.get_string('stoprecording', 'tinymce_recordrtc') +
234
                             ' (<span id="minutes"></span>:<span id="seconds"></span>)';
235
    M.tinymce_recordrtc.setTime();
236
    startStopBtn.disabled = false;
237
    countdownTicker = setInterval(M.tinymce_recordrtc.setTime, 1000);
238
};
239
240
M.tinymce_recordrtc.stopRecording = function(stream) {
241
    mediaRecorder.stop();
242
243
    stream.getTracks().forEach(function(track) {
244
        track.stop();
245
        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...
246
    });
247
248
    // Set source of audio player, then show it with controls enabled.
249
    // Not sure if necessary, need to figure out what formats the different browsers record in.
250
    /*
251
    if (webrtcDetectedBrowser === 'firefox') {
252
      var blob = new Blob(chunks, {type: 'audio/ogg'});
253
    } else {
254
      var blob = new Blob(chunks, {type: 'audio/wav'});
255
    }
256
    */
257
    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...
258
        type: 'audio/ogg;codecs=opus'
259
    });
260
    player.src = URL.createObjectURL(blob);
261
262
    player.muted = false;
263
    player.controls = true;
264
    player.classList.remove('hide');
265
    player.play();
266
267
    player.onended = function() {
268
        player.pause();
269
    };
270
271
    // Show upload button.
272
    uploadBtn.parentElement.classList.remove('hide');
273
    uploadBtn.textContent = M.util.get_string('attachrecording', 'tinymce_recordrtc');
274
    uploadBtn.disabled = false;
275
276
    // Handle when upload button is clicked.
277
    uploadBtn.onclick = function() {
278
        // Trigger error if no recording has been made.
279
        if (!player.src || chunks === []) {
280
            return alert(M.util.get_string('norecordingfound', 'tinymce_recordrtc'));
281
        }
282
283
        var btn = uploadBtn;
284
        btn.disabled = true;
285
286
        // Upload recording to server.
287
        M.tinymce_recordrtc.uploadToServer(function(progress, fileURL) {
288
            if (progress === 'ended') {
289
                btn.disabled = false;
290
                M.tinymce_recordrtc.insert_annotation(fileURL);
291
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
292
            } else if (progress === 'upload-failed') {
293
                btn.disabled = false;
294
                btn.textContent = M.util.get_string('uploadfailed', 'tinymce_recordrtc');
295
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
296
            } else {
297
                btn.textContent = progress;
298
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
299
            }
300
        });
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...
301
    };
302
};
303
304
// Upload recorded audio to server.
305
M.tinymce_recordrtc.uploadToServer = function(callback) {
306
    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...
307
308
    // Get src URL of audio tag.
309
    xhr.open('GET', player.src, true);
310
    xhr.responseType = 'blob';
311
312
    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...
313
        if (xhr.status === 200) {
314
            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...
315
316
            // Variable blob is now the blob that the audio tag's src pointed to.
317
            var blob = this.response;
318
            // Generate filename with random ID and file extension.
319
            var fileName = (Math.random() * 1000).toString().replace('.', '');
320
            // Not sure if necessary, need to figure out what formats the different browsers record in:
321
            // fileName += '.' + (webrtcDetectedBrowser === 'firefox' ? '-audio.ogg' : '-audio.wav');.
322
            fileName += '-audio.ogg';
323
324
            // Create FormData to send to PHP upload/save script.
325
            var formData = new FormData();
326
            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...
327
            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...
328
            formData.append('audio-filename', fileName);
329
            formData.append('audio-blob', blob);
330
331
            // Pass FormData to PHP script using XHR.
332
            M.tinymce_recordrtc.makeXMLHttpRequest('save.php', formData, function(progress, responseText) {
333
                if (progress === 'upload-ended') {
334
                    var initialURL = location.href.replace(location.href.split('/').pop(), '') + 'uploads.php/';
335
                    callback('ended', initialURL + responseText);
336
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
337
                } else {
338
                    callback(progress);
339
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
340
                }
341
            });
342
        }
343
    };
344
345
    xhr.send();
346
}
347
348
// Handle XHR sending/receiving/status.
349
M.tinymce_recordrtc.makeXMLHttpRequest = function(url, data, callback) {
350
    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...
351
352
    xhr.onreadystatechange = function() {
353
        // When request is finished and successful.
354
        if ((xhr.readyState === 4) && (xhr.status === 200)) {
355
            callback('upload-ended', xhr.responseText);
356
        } else if (xhr.status === 404) { // When request returns 404 Not Found.
357
            callback('upload-failed');
358
        }
359
    };
360
361
    xhr.upload.onprogress = function(event) {
362
        callback(Math.round(event.loaded / event.total * 100) + "% " +
363
                 M.util.get_string('uploadprogress', 'tinymce_recordrtc'));
364
    };
365
366
    xhr.upload.onerror = function(error) {
367
        callback('upload-failed');
368
        console.error('XMLHttpRequest failed: ', error);
369
    };
370
371
    xhr.upload.onabort = function(error) {
372
        callback(M.util.get_string('uploadaborted', 'tinymce_recordrtc'));
373
        console.error('XMLHttpRequest aborted: ', error);
374
    };
375
376
    // POST FormData to PHP script that handles uploading/saving.
377
    xhr.open('POST', url);
378
    xhr.send(data);
379
}
380
381
// Makes 1min and 2s display as 1:02 on timer instead of 1:2, for example.
382
M.tinymce_recordrtc.pad = function(val) {
383
    var valString = val + "";
384
385
    if (valString.length < 2) {
386
        return "0" + valString;
387
    } else {
388
        return valString;
389
    }
390
};
391
392
// Functionality to make recording timer count down.
393
// Also makes recording stop when time limit is hit.
394
M.tinymce_recordrtc.setTime = function() {
395
    countdownSeconds--;
396
397
    startStopBtn.querySelector('span#seconds').textContent = M.tinymce_recordrtc.pad(countdownSeconds % 60);
398
    startStopBtn.querySelector('span#minutes').textContent = M.tinymce_recordrtc.pad(parseInt(countdownSeconds / 60));
399
400
    if (countdownSeconds === 0) {
401
        startStopBtn.click();
402
    }
403
};
404
405
// Generates link to recorded annotation.
406
M.tinymce_recordrtc.create_annotation = function(recording_url) {
407
    // Create an icon linked to file in both editor text area and submission page:
408
    // var annotation = '<div id="recordrtc_annotation" class="text-center">...
409
    // ...<a target="_blank" href="' + recording_url + '"><img alt="RecordRTC Annotation"...
410
    // ...title="RecordRTC Annotation" src="' + recordrtc.icon32 + '" /></a></div>';.
411
412
    // Create link to file in editor text area and audio player in submission page.
413
    var annotation = '<div id="recordrtc_annotation" class="text-center"><a target="_blank" href="' + recording_url + '">' +
414
                     M.util.get_string('annotation', 'tinymce_recordrtc') + '</a></div>';
415
416
    return annotation;
417
};
418
419
// Inserts link to annotation in editor text area.
420
M.tinymce_recordrtc.insert_annotation = function(recording_url) {
421
    var annotation = M.tinymce_recordrtc.create_annotation(recording_url);
422
423
    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...
424
    tinyMCEPopup.close();
425
};
426