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/videomodule.js   C

Complexity

Total Complexity 58
Complexity/F 1.71

Size

Lines of Code 410
Function Count 34

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 0
nc 4224
dl 0
loc 410
rs 5.4871
c 3
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.create_annotation 0 12 1
A M.tinymce_recordrtc.stopRecording 0 54 1
A M.tinymce_recordrtc.setTime 0 10 2
A M.tinymce_recordrtc.insert_annotation 0 6 1
A M.tinymce_recordrtc.pad 0 9 2
B M.tinymce_recordrtc.makeXMLHttpRequest 0 31 1
B M.tinymce_recordrtc.uploadToServer 0 40 1
A videomodule.js ➔ d 0 3 1
B M.tinymce_recordrtc.view_init 0 124 2
A M.tinymce_recordrtc.captureUserMedia 0 3 1
A M.tinymce_recordrtc.handleStop 0 3 1
A M.tinymce_recordrtc.startRecording 0 19 1
B M.tinymce_recordrtc.captureAudioVideo 0 29 1
A M.tinymce_recordrtc.handleDataAvailable 0 3 1
B 0 21 5

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: 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('video#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 upload button is not shown.
84
            uploadBtn.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 video 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.classList.remove('hide');
132
                        alert.textContent = 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
            // Show video tag without controls to view webcam stream.
143
            player.classList.remove('hide');
144
            player.controls = false;
145
146
            // Capture audio+video stream from webcam/microphone.
147
            M.tinymce_recordrtc.captureAudioVideo(commonConfig);
148
149
            // When audio+video stream is successfully captured, start recording.
150
            btn.mediaCapturedCallback = function() {
151
                M.tinymce_recordrtc.startRecording(btn.stream);
152
            };
153
154
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
155
        } else { // If button is displaying "Stop Recording".
156
            // First of all clears the countdownTicker.
157
            clearInterval(countdownTicker);
158
159
            // Disable "Record Again" button for 1s to allow background processing (closing streams).
160
            setTimeout(function() {
161
                btn.disabled = false;
162
            }, 1000);
163
164
            // Stop recording.
165
            M.tinymce_recordrtc.stopRecording(btn.stream);
166
167
            // Change button to offer to record again.
168
            btn.textContent = M.util.get_string('recordagain', 'tinymce_recordrtc');
169
            startStopBtn.classList.remove('btn-danger');
170
            startStopBtn.classList.add('btn-outline-danger');
171
172
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
173
        }
174
    };
175
};
176
177
/////////////////////////
178
// Functions for capturing, recording, and uploading stream.
179
/////////////////////////
180
181
// Capture webcam/microphone stream.
182
M.tinymce_recordrtc.captureUserMedia = function(mediaConstraints, successCallback, errorCallback) {
183
    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...
184
};
185
186
// Setup to get audio+video stream from microphone/webcam.
187
M.tinymce_recordrtc.captureAudioVideo = function(config) {
188
    M.tinymce_recordrtc.captureUserMedia(
189
        // Media constraints.
190
        {
191
            audio: true,
192
            video: {
193
              width: {ideal: 640},
194
              height: {ideal: 480}
195
            }
196
        },
197
198
        // Success callback.
199
        function(audioVideoStream) {
200
            console.log('getUserMedia() got stream:', audioVideoStream);
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...
201
202
            // Set video player to play microphone+webcam stream.
203
            player.srcObject = audioVideoStream;
204
            player.play();
205
206
            config.onMediaCaptured(audioVideoStream);
207
        },
208
209
        // Error callback.
210
        function(error) {
211
            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...
212
            config.onMediaCapturingFailed(error);
213
        }
214
    );
215
};
216
217
// Add chunks of video to array when made available.
218
M.tinymce_recordrtc.handleDataAvailable = function(event) {
219
    chunks.push(event.data);
220
};
221
222
// Output information to console when recording stopped.
223
M.tinymce_recordrtc.handleStop = function(event) {
224
    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...
225
};
226
227
M.tinymce_recordrtc.startRecording = function(stream) {
228
    // Initialize recording of stream.
229
    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...
230
    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...
231
232
    mediaRecorder.ondataavailable = M.tinymce_recordrtc.handleDataAvailable;
233
    mediaRecorder.onstop = M.tinymce_recordrtc.handleStop;
234
    mediaRecorder.start(10); // Capture in 10ms chunks. Must be set to work with Firefox.
235
    console.log('MediaRecorder started:', mediaRecorder);
236
237
    // Set recording timer to the time specified in the settings.
238
    countdownSeconds = params['timelimit'];
239
    countdownSeconds++;
240
    startStopBtn.innerHTML = M.util.get_string('stoprecording', 'tinymce_recordrtc') +
241
                             ' (<span id="minutes"></span>:<span id="seconds"></span>)';
242
    M.tinymce_recordrtc.setTime();
243
    startStopBtn.disabled = false;
244
    countdownTicker = setInterval(M.tinymce_recordrtc.setTime, 1000);
245
};
246
247
M.tinymce_recordrtc.stopRecording = function(stream) {
248
    mediaRecorder.stop();
249
250
    stream.getTracks().forEach(function(track) {
251
        track.stop();
252
        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...
253
    });
254
255
    // Set source of video player, then show it with controls enabled.
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: 'video/webm;codecs=vp8'
258
    });
259
    player.src = URL.createObjectURL(blob);
260
261
    player.muted = false;
262
    player.controls = true;
263
    player.play();
264
265
    player.onended = function() {
266
        player.pause();
267
    };
268
269
    // Show upload button.
270
    uploadBtn.parentElement.classList.remove('hide');
271
    uploadBtn.textContent = M.util.get_string('attachrecording', 'tinymce_recordrtc');
272
    uploadBtn.disabled = false;
273
274
    // Handle when upload button is clicked.
275
    uploadBtn.onclick = function() {
276
        // Trigger error if no recording has been made.
277
        if (!player.src || chunks === []) {
278
            return alert(M.util.get_string('norecordingfound', 'tinymce_recordrtc'));
279
        }
280
281
        var btn = uploadBtn;
282
        btn.disabled = true;
283
284
        // Upload recording to server.
285
        M.tinymce_recordrtc.uploadToServer(function(progress, fileURL) {
286
            if (progress === 'ended') {
287
                btn.disabled = false;
288
                M.tinymce_recordrtc.insert_annotation(fileURL);
289
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
290
            } else if (progress === 'upload-failed') {
291
                btn.disabled = false;
292
                btn.textContent = M.util.get_string('uploadfailed', 'tinymce_recordrtc');
293
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
294
            } else {
295
                btn.textContent = progress;
296
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
297
            }
298
        });
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...
299
    };
300
};
301
302
// Upload recorded video to server.
303
M.tinymce_recordrtc.uploadToServer = function(callback) {
304
    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...
305
306
    // Get src URL of video tag.
307
    xhr.open('GET', player.src, true);
308
    xhr.responseType = 'blob';
309
310
    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...
311
        if (xhr.status === 200) {
312
            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...
313
314
            // Variable blob is now the blob that the video tag's src pointed to.
315
            var blob = this.response;
316
            // Generate filename with random ID and file extension.
317
            var fileName = (Math.random() * 1000).toString().replace('.', '');
318
            fileName += '-video.webm';
319
320
            // Create FormData to send to PHP upload/save script.
321
            var formData = new FormData();
322
            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...
323
            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...
324
            formData.append('video-filename', fileName);
325
            formData.append('video-blob', blob);
326
327
            // Pass FormData to PHP script using XHR.
328
            M.tinymce_recordrtc.makeXMLHttpRequest('save.php', formData, function(progress, responseText) {
329
                if (progress === 'upload-ended') {
330
                    var initialURL = location.href.replace(location.href.split('/').pop(), '') + 'uploads.php/';
331
                    callback('ended', initialURL + responseText);
332
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
333
                } else {
334
                    callback(progress);
335
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
336
                }
337
            });
338
        }
339
    };
340
341
    xhr.send();
342
}
343
344
// Handle XHR sending/receiving/status.
345
M.tinymce_recordrtc.makeXMLHttpRequest = function(url, data, callback) {
346
    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...
347
348
    xhr.onreadystatechange = function() {
349
        // When request is finished and successful.
350
        if ((xhr.readyState === 4) && (xhr.status === 200)) {
351
            callback('upload-ended', xhr.responseText);
352
        } else if (xhr.status === 404) { // When request returns 404 Not Found.
353
            callback('upload-failed');
354
        }
355
    };
356
357
    xhr.upload.onprogress = function(event) {
358
        callback(Math.round(event.loaded / event.total * 100) + "% " +
359
                 M.util.get_string('uploadprogress', 'tinymce_recordrtc'));
360
    };
361
362
    xhr.upload.onerror = function(error) {
363
        callback('upload-failed');
364
        console.error('XMLHttpRequest failed: ', error);
365
    };
366
367
    xhr.upload.onabort = function(error) {
368
        callback(M.util.get_string('uploadaborted', 'tinymce_recordrtc'));
369
        console.error('XMLHttpRequest aborted: ', error);
370
    };
371
372
    // POST FormData to PHP script that handles uploading/saving.
373
    xhr.open('POST', url);
374
    xhr.send(data);
375
}
376
377
// Makes 1min and 2s display as 1:02 on timer instead of 1:2, for example.
378
M.tinymce_recordrtc.pad = function(val) {
379
    var valString = val + "";
380
381
    if (valString.length < 2) {
382
        return "0" + valString;
383
    } else {
384
        return valString;
385
    }
386
};
387
388
// Functionality to make recording timer count down.
389
// Also makes recording stop when time limit is hit.
390
M.tinymce_recordrtc.setTime = function() {
391
    countdownSeconds--;
392
393
    startStopBtn.querySelector('span#seconds').textContent = M.tinymce_recordrtc.pad(countdownSeconds % 60);
394
    startStopBtn.querySelector('span#minutes').textContent = M.tinymce_recordrtc.pad(parseInt(countdownSeconds / 60));
395
396
    if (countdownSeconds === 0) {
397
        startStopBtn.click();
398
    }
399
};
400
401
// Generates link to recorded annotation.
402
M.tinymce_recordrtc.create_annotation = function(recording_url) {
403
    // Create an icon linked to file in both editor text area and submission page:
404
    // var annotation = '<div id="recordrtc_annotation" class="text-center">...
405
    // ...<a target="_blank" href="' + recording_url + '"><img alt="RecordRTC Annotation"...
406
    // ...title="RecordRTC Annotation" src="' + recordrtc.icon32 + '" /></a></div>';.
407
408
    // Create link to file in editor text area and video player in submission page.
409
    var annotation = '<div id="recordrtc_annotation" class="text-center"><a target="_blank" href="' + recording_url + '">' +
410
                     M.util.get_string('annotation', 'tinymce_recordrtc') + '</a></div>';
411
412
    return annotation;
413
};
414
415
// Inserts link to annotation in editor text area.
416
M.tinymce_recordrtc.insert_annotation = function(recording_url) {
417
    var annotation = M.tinymce_recordrtc.create_annotation(recording_url);
418
419
    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...
420
    tinyMCEPopup.close();
421
};
422